| // Copyright 2018 The Chromium Authors | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "content/browser/renderer_host/navigation_throttle_runner.h" | 
 |  | 
 | #include <optional> | 
 | #include <set> | 
 |  | 
 | #include "base/functional/bind.h" | 
 | #include "base/metrics/metrics_hashes.h" | 
 | #include "components/ukm/test_ukm_recorder.h" | 
 | #include "content/browser/renderer_host/navigation_throttle_registry_impl.h" | 
 | #include "content/public/browser/navigation_throttle.h" | 
 | #include "content/public/common/url_constants.h" | 
 | #include "content/public/test/mock_navigation_handle.h" | 
 | #include "content/public/test/test_navigation_throttle.h" | 
 | #include "content/public/test/test_renderer_host.h" | 
 | #include "services/metrics/public/cpp/ukm_builders.h" | 
 | #include "testing/gmock/include/gmock/gmock.h" | 
 |  | 
 | namespace content { | 
 |  | 
 | // Test version of a NavigationThrottle that will execute a callback when | 
 | // called. | 
 | class DeletingNavigationThrottle : public NavigationThrottle { | 
 |  public: | 
 |   DeletingNavigationThrottle(NavigationThrottleRegistry& registry, | 
 |                              const base::RepeatingClosure& deletion_callback) | 
 |       : NavigationThrottle(registry), deletion_callback_(deletion_callback) {} | 
 |   ~DeletingNavigationThrottle() override {} | 
 |  | 
 |   NavigationThrottle::ThrottleCheckResult WillStartRequest() override { | 
 |     deletion_callback_.Run(); | 
 |     return NavigationThrottle::PROCEED; | 
 |   } | 
 |  | 
 |   NavigationThrottle::ThrottleCheckResult WillRedirectRequest() override { | 
 |     deletion_callback_.Run(); | 
 |     return NavigationThrottle::PROCEED; | 
 |   } | 
 |  | 
 |   NavigationThrottle::ThrottleCheckResult WillFailRequest() override { | 
 |     deletion_callback_.Run(); | 
 |     return NavigationThrottle::PROCEED; | 
 |   } | 
 |  | 
 |   NavigationThrottle::ThrottleCheckResult WillProcessResponse() override { | 
 |     deletion_callback_.Run(); | 
 |     return NavigationThrottle::PROCEED; | 
 |   } | 
 |  | 
 |   NavigationThrottle::ThrottleCheckResult WillCommitWithoutUrlLoader() | 
 |       override { | 
 |     deletion_callback_.Run(); | 
 |     return NavigationThrottle::PROCEED; | 
 |   } | 
 |  | 
 |   const char* GetNameForLogging() override { | 
 |     return "DeletingNavigationThrottle"; | 
 |   } | 
 |  | 
 |  private: | 
 |   base::RepeatingClosure deletion_callback_; | 
 | }; | 
 |  | 
 | class NavigationThrottleRunnerTest : public RenderViewHostTestHarness, | 
 |                                      public NavigationThrottleRegistryBase { | 
 |  public: | 
 |   NavigationThrottleRunnerTest() | 
 |       : delegate_result_(NavigationThrottle::DEFER) {} | 
 |   ~NavigationThrottleRunnerTest() override = default; | 
 |  | 
 |   void SetUp() override { | 
 |     RenderViewHostTestHarness::SetUp(); | 
 |     runner_ = std::make_unique<NavigationThrottleRunner>(this, 1, true); | 
 |   } | 
 |  | 
 |   void Resume() { | 
 |     ASSERT_EQ(1u, deferring_throttles_.size()); | 
 |     runner_->ResumeProcessingNavigationEvent(*deferring_throttles_.begin()); | 
 |     deferring_throttles_.clear(); | 
 |   } | 
 |  | 
 |   void SimulateEvent(NavigationThrottleEvent event) { | 
 |     was_delegate_notified_ = false; | 
 |     delegate_result_ = NavigationThrottle::DEFER; | 
 |     observer_last_event_ = NavigationThrottleEvent::kNoEvent; | 
 |     runner_->ProcessNavigationEvent(event); | 
 |   } | 
 |  | 
 |   // Whether the callback was called. | 
 |   bool was_delegate_notified() const { return was_delegate_notified_; } | 
 |  | 
 |   // Returns the delegate_result. | 
 |   NavigationThrottle::ThrottleCheckResult delegate_result() const { | 
 |     return delegate_result_; | 
 |   } | 
 |  | 
 |   NavigationThrottleEvent observer_last_event() const { | 
 |     return observer_last_event_; | 
 |   } | 
 |  | 
 |   bool is_deferring() { return !deferring_throttles_.empty(); } | 
 |  | 
 |   NavigationThrottleRunner* runner() { return runner_.get(); } | 
 |  | 
 |   void CheckNotNotified(TestNavigationThrottle* throttle) { | 
 |     CHECK_EQ( | 
 |         0, throttle->GetCallCount(TestNavigationThrottle::WILL_START_REQUEST)); | 
 |     CHECK_EQ(0, throttle->GetCallCount( | 
 |                     TestNavigationThrottle::WILL_REDIRECT_REQUEST)); | 
 |     CHECK_EQ(0, | 
 |              throttle->GetCallCount(TestNavigationThrottle::WILL_FAIL_REQUEST)); | 
 |     CHECK_EQ(0, throttle->GetCallCount( | 
 |                     TestNavigationThrottle::WILL_PROCESS_RESPONSE)); | 
 |   } | 
 |  | 
 |   void CheckNotifiedOfEvent(TestNavigationThrottle* throttle, | 
 |                             NavigationThrottleEvent event) { | 
 |     if (event == NavigationThrottleEvent::kWillStartRequest) { | 
 |       CHECK_EQ(1, throttle->GetCallCount( | 
 |                       TestNavigationThrottle::WILL_START_REQUEST)); | 
 |     } else { | 
 |       CHECK_EQ(0, throttle->GetCallCount( | 
 |                       TestNavigationThrottle::WILL_START_REQUEST)); | 
 |     } | 
 |     if (event == NavigationThrottleEvent::kWillRedirectRequest) { | 
 |       CHECK_EQ(1, throttle->GetCallCount( | 
 |                       TestNavigationThrottle::WILL_REDIRECT_REQUEST)); | 
 |     } else { | 
 |       CHECK_EQ(0, throttle->GetCallCount( | 
 |                       TestNavigationThrottle::WILL_REDIRECT_REQUEST)); | 
 |     } | 
 |     if (event == NavigationThrottleEvent::kWillFailRequest) { | 
 |       CHECK_EQ( | 
 |           1, throttle->GetCallCount(TestNavigationThrottle::WILL_FAIL_REQUEST)); | 
 |     } else { | 
 |       CHECK_EQ( | 
 |           0, throttle->GetCallCount(TestNavigationThrottle::WILL_FAIL_REQUEST)); | 
 |     } | 
 |     if (event == NavigationThrottleEvent::kWillProcessResponse) { | 
 |       CHECK_EQ(1, throttle->GetCallCount( | 
 |                       TestNavigationThrottle::WILL_PROCESS_RESPONSE)); | 
 |     } else { | 
 |       CHECK_EQ(0, throttle->GetCallCount( | 
 |                       TestNavigationThrottle::WILL_PROCESS_RESPONSE)); | 
 |     } | 
 |     if (event == NavigationThrottleEvent::kWillCommitWithoutUrlLoader) { | 
 |       CHECK_EQ(1, throttle->GetCallCount( | 
 |                       TestNavigationThrottle::WILL_COMMIT_WITHOUT_URL_LOADER)); | 
 |     } else { | 
 |       CHECK_EQ(0, throttle->GetCallCount( | 
 |                       TestNavigationThrottle::WILL_COMMIT_WITHOUT_URL_LOADER)); | 
 |     } | 
 |   } | 
 |  | 
 |   // Creates, register and returns a TestNavigationThrottle that will | 
 |   // synchronously return |result| on checks by default. | 
 |   TestNavigationThrottle* CreateTestNavigationThrottle( | 
 |       NavigationThrottle::ThrottleCheckResult result) { | 
 |     TestNavigationThrottle* test_throttle = new TestNavigationThrottle(*this); | 
 |     test_throttle->SetResponseForAllMethods(TestNavigationThrottle::SYNCHRONOUS, | 
 |                                             result); | 
 |     AddThrottle(std::unique_ptr<TestNavigationThrottle>(test_throttle)); | 
 |     return test_throttle; | 
 |   } | 
 |  | 
 |   // Creates, register and returns a TestNavigationThrottle that will | 
 |   // synchronously return |result| on check for the given |method|, and | 
 |   // NavigationThrottle::PROCEED otherwise. | 
 |   TestNavigationThrottle* CreateTestNavigationThrottle( | 
 |       TestNavigationThrottle::ThrottleMethod method, | 
 |       NavigationThrottle::ThrottleCheckResult result) { | 
 |     TestNavigationThrottle* test_throttle = | 
 |         CreateTestNavigationThrottle(NavigationThrottle::PROCEED); | 
 |     test_throttle->SetResponse(method, TestNavigationThrottle::SYNCHRONOUS, | 
 |                                result); | 
 |     return test_throttle; | 
 |   } | 
 |  | 
 |   // Creates and register a NavigationThrottle that will delete the | 
 |   // NavigationHandle in checks. | 
 |   void AddDeletingNavigationThrottle() { | 
 |     AddThrottle(std::make_unique<DeletingNavigationThrottle>( | 
 |         *this, base::BindRepeating( | 
 |                    &NavigationThrottleRunnerTest::ResetNavigationThrottleRunner, | 
 |                    base::Unretained(this)))); | 
 |   } | 
 |  | 
 |   ukm::TestUkmRecorder& test_ukm_recorder() { return test_ukm_recorder_; } | 
 |  | 
 |  private: | 
 |   // NavigationThrottleRegistry: | 
 |   NavigationHandle& GetNavigationHandle() override { return handle_; } | 
 |   void AddThrottle( | 
 |       std::unique_ptr<NavigationThrottle> navigation_throttle) override { | 
 |     throttles_.push_back(std::move(navigation_throttle)); | 
 |   } | 
 |   bool IsHTTPOrHTTPS() override { | 
 |     return handle_.GetURL().SchemeIsHTTPOrHTTPS(); | 
 |   } | 
 |   MOCK_METHOD(bool, HasThrottle, (const std::string& name), (override)); | 
 |   MOCK_METHOD(bool, EraseThrottleForTesting, (const std::string& name), | 
 |               (override)); | 
 |  | 
 |   // NavigationThrottleRegistryBase: | 
 |   void OnEventProcessed( | 
 |       NavigationThrottleEvent event, | 
 |       NavigationThrottle::ThrottleCheckResult result) override { | 
 |     DCHECK(!was_delegate_notified_); | 
 |     delegate_result_ = result; | 
 |     was_delegate_notified_ = true; | 
 |     observer_last_event_ = event; | 
 |   } | 
 |   void OnDeferProcessingNavigationEvent( | 
 |       NavigationThrottle* deferring_throttle) override { | 
 |     deferring_throttles_.insert(deferring_throttle); | 
 |   } | 
 |   std::vector<std::unique_ptr<NavigationThrottle>>& GetThrottles() override { | 
 |     return throttles_; | 
 |   } | 
 |   NavigationThrottle& GetThrottleAtIndex(size_t index) override { | 
 |     EXPECT_LT(index, throttles_.size()); | 
 |     return *throttles_[index]; | 
 |   } | 
 |   const std::set<NavigationThrottle*>& GetDeferringThrottles() const override { | 
 |     return deferring_throttles_; | 
 |   } | 
 |  | 
 |   void ResetNavigationThrottleRunner() { runner_.reset(); } | 
 |  | 
 |   MockNavigationHandle handle_; | 
 |   std::vector<std::unique_ptr<NavigationThrottle>> throttles_; | 
 |   std::set<NavigationThrottle*> deferring_throttles_; | 
 |   std::unique_ptr<NavigationThrottleRunner> runner_; | 
 |   NavigationThrottleEvent observer_last_event_ = | 
 |       NavigationThrottleEvent::kNoEvent; | 
 |   bool was_delegate_notified_ = false; | 
 |   NavigationThrottle::ThrottleCheckResult delegate_result_; | 
 |   ukm::TestAutoSetUkmRecorder test_ukm_recorder_; | 
 | }; | 
 |  | 
 | class NavigationThrottleRunnerTestWithEvent | 
 |     : public NavigationThrottleRunnerTest, | 
 |       public testing::WithParamInterface<NavigationThrottleEvent> { | 
 |  public: | 
 |   NavigationThrottleRunnerTestWithEvent() = default; | 
 |   ~NavigationThrottleRunnerTestWithEvent() override = default; | 
 |   void SetUp() override { | 
 |     NavigationThrottleRunnerTest::SetUp(); | 
 |     event_ = GetParam(); | 
 |   } | 
 |  | 
 |   NavigationThrottleEvent event() const { return event_; } | 
 |  | 
 |   void CheckNotified(TestNavigationThrottle* throttle) { | 
 |     CheckNotifiedOfEvent(throttle, event()); | 
 |   } | 
 |  | 
 |  private: | 
 |   NavigationThrottleEvent event_; | 
 | }; | 
 |  | 
 | // Checks that a navigation deferred by a NavigationThrottle can be properly | 
 | // resumed. | 
 | TEST_P(NavigationThrottleRunnerTestWithEvent, ResumeDeferred) { | 
 |   TestNavigationThrottle* test_throttle = | 
 |       CreateTestNavigationThrottle(NavigationThrottle::DEFER); | 
 |   CheckNotNotified(test_throttle); | 
 |  | 
 |   // Simulate the event. The request should be deferred. The observer | 
 |   // should not have been notified. | 
 |   SimulateEvent(event()); | 
 |   CheckNotified(test_throttle); | 
 |   EXPECT_TRUE(is_deferring()); | 
 |   EXPECT_FALSE(was_delegate_notified()); | 
 |  | 
 |   // Resume the request. It should no longer be deferred and the observer | 
 |   // should have been notified. | 
 |   Resume(); | 
 |   CheckNotified(test_throttle); | 
 |   EXPECT_FALSE(is_deferring()); | 
 |   EXPECT_TRUE(was_delegate_notified()); | 
 |   EXPECT_EQ(NavigationThrottle::PROCEED, delegate_result()); | 
 |   EXPECT_EQ(event(), observer_last_event()); | 
 | } | 
 |  | 
 | // Checks that a NavigationThrottleRunner can be safely deleted by the execution | 
 | // of one of its NavigationThrottle. | 
 | TEST_P(NavigationThrottleRunnerTestWithEvent, DeletionByNavigationThrottle) { | 
 |   AddDeletingNavigationThrottle(); | 
 |   SimulateEvent(event()); | 
 |   EXPECT_EQ(nullptr, runner()); | 
 |   EXPECT_FALSE(was_delegate_notified()); | 
 | } | 
 |  | 
 | // Checks that a NavigationThrottleRunner can be safely deleted by the execution | 
 | // of one of its NavigationThrottle following a call to | 
 | // ResumeProcessingNavigationEvent. | 
 | TEST_P(NavigationThrottleRunnerTestWithEvent, | 
 |        DeletionByNavigationThrottleAfterResume) { | 
 |   CreateTestNavigationThrottle(NavigationThrottle::DEFER); | 
 |   AddDeletingNavigationThrottle(); | 
 |   SimulateEvent(event()); | 
 |   EXPECT_NE(nullptr, runner()); | 
 |   Resume(); | 
 |   EXPECT_EQ(nullptr, runner()); | 
 |   EXPECT_FALSE(was_delegate_notified()); | 
 | } | 
 |  | 
 | INSTANTIATE_TEST_SUITE_P( | 
 |     AllEvents, | 
 |     NavigationThrottleRunnerTestWithEvent, | 
 |     ::testing::Values(NavigationThrottleEvent::kWillStartRequest, | 
 |                       NavigationThrottleEvent::kWillRedirectRequest, | 
 |                       NavigationThrottleEvent::kWillFailRequest, | 
 |                       NavigationThrottleEvent::kWillProcessResponse, | 
 |                       NavigationThrottleEvent::kWillCommitWithoutUrlLoader)); | 
 |  | 
 | class NavigationThrottleRunnerTestWithEventAndAction | 
 |     : public NavigationThrottleRunnerTest, | 
 |       public testing::WithParamInterface< | 
 |           std::tuple<NavigationThrottleEvent, | 
 |                      NavigationThrottle::ThrottleAction>> { | 
 |  public: | 
 |   NavigationThrottleRunnerTestWithEventAndAction() = default; | 
 |   ~NavigationThrottleRunnerTestWithEventAndAction() override = default; | 
 |   void SetUp() override { | 
 |     NavigationThrottleRunnerTest::SetUp(); | 
 |     std::tie(event_, action_) = GetParam(); | 
 |   } | 
 |   NavigationThrottleEvent event() const { return event_; } | 
 |   NavigationThrottle::ThrottleAction action() const { return action_; } | 
 |  | 
 |   void CheckNotified(TestNavigationThrottle* throttle) { | 
 |     CheckNotifiedOfEvent(throttle, event()); | 
 |   } | 
 |  | 
 |  private: | 
 |   NavigationThrottleEvent event_; | 
 |   NavigationThrottle::ThrottleAction action_; | 
 | }; | 
 |  | 
 | // Checks that a NavigationThrottle asking during to defer | 
 | // followed by another NavigationThrottle behave correctly. | 
 | TEST_P(NavigationThrottleRunnerTestWithEventAndAction, DeferThenAction) { | 
 |   TestNavigationThrottle* defer_throttle = | 
 |       CreateTestNavigationThrottle(NavigationThrottle::DEFER); | 
 |   TestNavigationThrottle* test_throttle = | 
 |       CreateTestNavigationThrottle(action()); | 
 |   CheckNotNotified(defer_throttle); | 
 |   CheckNotNotified(test_throttle); | 
 |  | 
 |   // Simulate the event. The request should be deferred. The observer | 
 |   // should not have been notified. The second throttle should not have been | 
 |   // notified. | 
 |   SimulateEvent(event()); | 
 |   CheckNotified(defer_throttle); | 
 |   CheckNotNotified(test_throttle); | 
 |   EXPECT_TRUE(is_deferring()); | 
 |   EXPECT_FALSE(was_delegate_notified()); | 
 |  | 
 |   // Resume the request. It should no longer be deferred and the observer | 
 |   // should have been notified. The second throttle should have been notified. | 
 |   Resume(); | 
 |   CheckNotified(defer_throttle); | 
 |   CheckNotified(test_throttle); | 
 |   EXPECT_FALSE(is_deferring()); | 
 |   EXPECT_TRUE(was_delegate_notified()); | 
 |   EXPECT_EQ(action(), delegate_result().action()); | 
 |   EXPECT_EQ(event(), observer_last_event()); | 
 | } | 
 |  | 
 | // Checks that a NavigationThrottle asking to cancel followed by a | 
 | // NavigationThrottle asking to proceed behave correctly. The navigation will | 
 | // be stopped directly, and the second throttle will not be called. | 
 | TEST_P(NavigationThrottleRunnerTestWithEventAndAction, CancelThenProceed) { | 
 |   if (action() == NavigationThrottle::PROCEED) { | 
 |     return; | 
 |   } | 
 |   TestNavigationThrottle* test_throttle = | 
 |       CreateTestNavigationThrottle(action()); | 
 |   TestNavigationThrottle* proceed_throttle = | 
 |       CreateTestNavigationThrottle(NavigationThrottle::PROCEED); | 
 |   CheckNotNotified(test_throttle); | 
 |   CheckNotNotified(proceed_throttle); | 
 |  | 
 |   // Simulate the event. The request should be not be deferred. The observer | 
 |   // should have been notified. The second throttle should not have been | 
 |   // notified. | 
 |   SimulateEvent(event()); | 
 |   CheckNotified(test_throttle); | 
 |   CheckNotNotified(proceed_throttle); | 
 |   EXPECT_FALSE(is_deferring()); | 
 |   EXPECT_TRUE(was_delegate_notified()); | 
 |   EXPECT_EQ(action(), delegate_result().action()); | 
 |   EXPECT_EQ(event(), observer_last_event()); | 
 | } | 
 |  | 
 | // Checks that a NavigationThrottle asking to proceed followed by a | 
 | // NavigationThrottle asking to cancel behave correctly. | 
 | // Both throttles will be called, and the request will be cancelled. | 
 | TEST_P(NavigationThrottleRunnerTestWithEventAndAction, ProceedThenCancel) { | 
 |   if (action() == NavigationThrottle::PROCEED) { | 
 |     return; | 
 |   } | 
 |   TestNavigationThrottle* proceed_throttle = | 
 |       CreateTestNavigationThrottle(NavigationThrottle::PROCEED); | 
 |   TestNavigationThrottle* test_throttle = | 
 |       CreateTestNavigationThrottle(action()); | 
 |   CheckNotNotified(test_throttle); | 
 |   CheckNotNotified(proceed_throttle); | 
 |  | 
 |   // Simulate the event. The request should be not be deferred. The observer | 
 |   // should have been notified. | 
 |   SimulateEvent(event()); | 
 |   CheckNotified(proceed_throttle); | 
 |   CheckNotified(test_throttle); | 
 |   EXPECT_FALSE(is_deferring()); | 
 |   EXPECT_TRUE(was_delegate_notified()); | 
 |   EXPECT_EQ(action(), delegate_result().action()); | 
 |   EXPECT_EQ(event(), observer_last_event()); | 
 | } | 
 |  | 
 | // Checks that a NavigationThrottle being deferred and resumed records UKM about | 
 | // the deferral. | 
 | TEST_P(NavigationThrottleRunnerTestWithEventAndAction, DeferRecordsUKM) { | 
 |   TestNavigationThrottle* defer_throttle = | 
 |       CreateTestNavigationThrottle(NavigationThrottle::DEFER); | 
 |   CheckNotNotified(defer_throttle); | 
 |  | 
 |   // Simulate the event. The request should be deferred. | 
 |   SimulateEvent(event()); | 
 |   CheckNotified(defer_throttle); | 
 |   EXPECT_TRUE(is_deferring()); | 
 |  | 
 |   // Resume the request. This should record UKM. | 
 |   Resume(); | 
 |  | 
 |   // There should be one entry with name hash matching the logging name and | 
 |   // event that is being run. Ignore the time for testing as it is variable, and | 
 |   // even possibly 0. | 
 |   const auto& entries = test_ukm_recorder().GetEntriesByName( | 
 |       ukm::builders::NavigationThrottleDeferredTime::kEntryName); | 
 |   EXPECT_EQ(1u, entries.size()); | 
 |   for (const ukm::mojom::UkmEntry* entry : entries) { | 
 |     EXPECT_EQ(*ukm::TestUkmRecorder::GetEntryMetric( | 
 |                   entry, ukm::builders::NavigationThrottleDeferredTime:: | 
 |                              kNavigationThrottleEventTypeName), | 
 |               static_cast<int64_t>(event())); | 
 |     EXPECT_EQ(*ukm::TestUkmRecorder::GetEntryMetric( | 
 |                   entry, ukm::builders::NavigationThrottleDeferredTime:: | 
 |                              kNavigationThrottleNameHashName), | 
 |               static_cast<int64_t>( | 
 |                   base::HashMetricName(defer_throttle->GetNameForLogging()))); | 
 |   } | 
 | } | 
 |  | 
 | INSTANTIATE_TEST_SUITE_P( | 
 |     AllEvents, | 
 |     NavigationThrottleRunnerTestWithEventAndAction, | 
 |     ::testing::Combine( | 
 |         ::testing::Values(NavigationThrottleEvent::kWillStartRequest, | 
 |                           NavigationThrottleEvent::kWillRedirectRequest, | 
 |                           NavigationThrottleEvent::kWillFailRequest, | 
 |                           NavigationThrottleEvent::kWillProcessResponse, | 
 |                           NavigationThrottleEvent::kWillCommitWithoutUrlLoader), | 
 |         ::testing::Values(NavigationThrottle::PROCEED, | 
 |                           NavigationThrottle::CANCEL, | 
 |                           NavigationThrottle::CANCEL_AND_IGNORE, | 
 |                           NavigationThrottle::BLOCK_REQUEST, | 
 |                           NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE, | 
 |                           NavigationThrottle::BLOCK_RESPONSE))); | 
 |  | 
 | class NavigationThrottleRunnerTestWithEventAndError | 
 |     : public NavigationThrottleRunnerTest, | 
 |       public testing::WithParamInterface< | 
 |           std::tuple<NavigationThrottleEvent, | 
 |                      net::Error, | 
 |                      std::optional<std::string>>> { | 
 |  public: | 
 |   NavigationThrottleRunnerTestWithEventAndError() = default; | 
 |   ~NavigationThrottleRunnerTestWithEventAndError() override = default; | 
 |   void SetUp() override { | 
 |     NavigationThrottleRunnerTest::SetUp(); | 
 |     std::tie(event_, error_, custom_error_page_) = GetParam(); | 
 |   } | 
 |   NavigationThrottleEvent event() const { return event_; } | 
 |   net::Error error() const { return error_; } | 
 |   const std::optional<std::string>& custom_error_page() const { | 
 |     return custom_error_page_; | 
 |   } | 
 |  | 
 |   void CheckNotified(TestNavigationThrottle* throttle) { | 
 |     CheckNotifiedOfEvent(throttle, event()); | 
 |   } | 
 |  | 
 |  private: | 
 |   NavigationThrottleEvent event_; | 
 |   net::Error error_; | 
 |   std::optional<std::string> custom_error_page_ = std::nullopt; | 
 | }; | 
 |  | 
 | // Checks that the NavigationThrottleRunner correctly propagates a | 
 | // ThrottleCheckResult with a custom error page and/or error code to its | 
 | // delegate. | 
 | TEST_P(NavigationThrottleRunnerTestWithEventAndError, CustomNetError) { | 
 |   SCOPED_TRACE(::testing::Message() | 
 |                << "Event: " << static_cast<int>(event()) | 
 |                << " Error: " << error() << " Custom error page: " | 
 |                << (custom_error_page().has_value() ? custom_error_page().value() | 
 |                                                    : "")); | 
 |   NavigationThrottle::ThrottleCheckResult result(NavigationThrottle::CANCEL, | 
 |                                                  error()); | 
 |   if (custom_error_page().has_value()) { | 
 |     result = NavigationThrottle::ThrottleCheckResult( | 
 |         NavigationThrottle::CANCEL, error(), custom_error_page().value()); | 
 |   } | 
 |   TestNavigationThrottle* test_throttle = CreateTestNavigationThrottle(result); | 
 |   CheckNotNotified(test_throttle); | 
 |  | 
 |   // Simulate the event. The request should not be deferred. The | 
 |   // callback should have been called. | 
 |   SimulateEvent(event()); | 
 |   EXPECT_FALSE(is_deferring()); | 
 |   EXPECT_TRUE(was_delegate_notified()); | 
 |   EXPECT_EQ(NavigationThrottle::CANCEL, delegate_result().action()); | 
 |   EXPECT_EQ(error(), delegate_result().net_error_code()); | 
 |   EXPECT_EQ(custom_error_page().has_value(), | 
 |             delegate_result().error_page_content().has_value()); | 
 |   if (custom_error_page().has_value()) { | 
 |     EXPECT_EQ(custom_error_page().value(), | 
 |               delegate_result().error_page_content().value()); | 
 |   } | 
 |   CheckNotified(test_throttle); | 
 | } | 
 |  | 
 | INSTANTIATE_TEST_SUITE_P( | 
 |     AllEvents, | 
 |     NavigationThrottleRunnerTestWithEventAndError, | 
 |     ::testing::Combine( | 
 |         ::testing::Values(NavigationThrottleEvent::kWillStartRequest, | 
 |                           NavigationThrottleEvent::kWillRedirectRequest, | 
 |                           NavigationThrottleEvent::kWillFailRequest, | 
 |                           NavigationThrottleEvent::kWillProcessResponse, | 
 |                           NavigationThrottleEvent::kWillCommitWithoutUrlLoader), | 
 |         ::testing::Values(net::ERR_BLOCKED_BY_ADMINISTRATOR, net::ERR_ABORTED), | 
 |         ::testing::Values(std::nullopt, "<html><body>test</body></html>"))); | 
 |  | 
 | }  // namespace content |