ios_blocking_page_tab_helper_unittest.mm 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #import "ios/components/security_interstitials/ios_blocking_page_tab_helper.h"
  5. #include "ios/components/security_interstitials/ios_security_interstitial_page.h"
  6. #import "ios/web/public/test/fakes/fake_navigation_context.h"
  7. #import "ios/web/public/test/fakes/fake_web_state.h"
  8. #include "testing/platform_test.h"
  9. #include "url/gurl.h"
  10. #if !defined(__has_feature) || !__has_feature(objc_arc)
  11. #error "This file requires ARC support."
  12. #endif
  13. namespace security_interstitials {
  14. class TestInterstitialPage : public IOSSecurityInterstitialPage {
  15. public:
  16. // |*destroyed_tracker| is set to true in the destructor.
  17. TestInterstitialPage(web::WebState* web_state,
  18. const GURL& request_url,
  19. bool* destroyed_tracker)
  20. : IOSSecurityInterstitialPage(web_state,
  21. request_url,
  22. /*argument_name=*/nullptr),
  23. destroyed_tracker_(destroyed_tracker) {}
  24. ~TestInterstitialPage() override {
  25. if (destroyed_tracker_)
  26. *destroyed_tracker_ = true;
  27. }
  28. private:
  29. void HandleCommand(SecurityInterstitialCommand command,
  30. const GURL& origin_url,
  31. bool user_is_interacting,
  32. web::WebFrame* sender_frame) override {}
  33. bool ShouldCreateNewNavigation() const override { return false; }
  34. void PopulateInterstitialStrings(
  35. base::Value::Dict& load_time_data) const override {}
  36. bool* destroyed_tracker_ = nullptr;
  37. };
  38. class IOSBlockingPageTabHelperTest : public PlatformTest {
  39. protected:
  40. IOSBlockingPageTabHelperTest() {
  41. IOSBlockingPageTabHelper::CreateForWebState(&web_state_);
  42. }
  43. std::unique_ptr<web::NavigationContext> CreateContext(bool committed,
  44. bool is_same_document) {
  45. std::unique_ptr<web::FakeNavigationContext> context =
  46. std::make_unique<web::FakeNavigationContext>();
  47. context->SetHasCommitted(committed);
  48. context->SetIsSameDocument(is_same_document);
  49. return context;
  50. }
  51. IOSBlockingPageTabHelper* helper() {
  52. return IOSBlockingPageTabHelper::FromWebState(&web_state_);
  53. }
  54. // Creates a blocking page and associates it with |context|'s navigation ID
  55. // in the tab helper. Returns the created blocking page. |destroyed_tracker|
  56. // is an out-parameter that is reset to true when the blocking page is
  57. // destroyed.
  58. IOSSecurityInterstitialPage* CreateAssociatedBlockingPage(
  59. web::NavigationContext* context,
  60. bool* destroyed_tracker) {
  61. std::unique_ptr<IOSSecurityInterstitialPage> passed_blocking_page =
  62. std::make_unique<TestInterstitialPage>(&web_state_, GURL(),
  63. destroyed_tracker);
  64. IOSSecurityInterstitialPage* blocking_page = passed_blocking_page.get();
  65. helper()->AssociateBlockingPage(context->GetNavigationId(),
  66. std::move(passed_blocking_page));
  67. return blocking_page;
  68. }
  69. web::FakeWebState web_state_;
  70. };
  71. // Tests that the helper properly handles the lifetime of a single blocking
  72. // page, interleaved with other navigations.
  73. TEST_F(IOSBlockingPageTabHelperTest, SingleBlockingPage) {
  74. std::unique_ptr<web::NavigationContext> blocking_page_context =
  75. CreateContext(/*committed=*/true, /*is_same_document=*/false);
  76. bool blocking_page_destroyed = false;
  77. CreateAssociatedBlockingPage(blocking_page_context.get(),
  78. &blocking_page_destroyed);
  79. // Test that a same-document navigation doesn't destroy the blocking page if
  80. // its navigation hasn't committed yet.
  81. std::unique_ptr<web::NavigationContext> same_document_context =
  82. CreateContext(/*committed=*/true, /*is_same_document=*/true);
  83. web_state_.OnNavigationFinished(same_document_context.get());
  84. EXPECT_FALSE(blocking_page_destroyed);
  85. // Test that a committed (non-same-document) navigation doesn't destroy the
  86. // blocking page if its navigation hasn't committed yet.
  87. std::unique_ptr<web::NavigationContext> committed_context1 =
  88. CreateContext(/*committed=*/true, /*is_same_document=*/false);
  89. web_state_.OnNavigationFinished(committed_context1.get());
  90. EXPECT_FALSE(blocking_page_destroyed);
  91. // Simulate committing the interstitial.
  92. web_state_.OnNavigationFinished(blocking_page_context.get());
  93. EXPECT_FALSE(blocking_page_destroyed);
  94. // Test that a subsequent committed navigation releases the blocking page
  95. // stored for the currently committed navigation.
  96. std::unique_ptr<web::NavigationContext> committed_context2 =
  97. CreateContext(/*committed=*/true, /*is_same_document=*/false);
  98. web_state_.OnNavigationFinished(committed_context2.get());
  99. EXPECT_TRUE(blocking_page_destroyed);
  100. }
  101. // Tests that the helper properly handles the lifetime of multiple blocking
  102. // pages, committed in a different order than they are created.
  103. TEST_F(IOSBlockingPageTabHelperTest, MultipleBlockingPage) {
  104. // Simulate associating the first interstitial.
  105. std::unique_ptr<web::NavigationContext> context1 =
  106. CreateContext(/*committed=*/true, /*is_same_document=*/false);
  107. bool blocking_page1_destroyed = false;
  108. CreateAssociatedBlockingPage(context1.get(), &blocking_page1_destroyed);
  109. // Simulate commiting the first interstitial.
  110. web_state_.OnNavigationFinished(context1.get());
  111. EXPECT_FALSE(blocking_page1_destroyed);
  112. // Associate the second interstitial.
  113. std::unique_ptr<web::NavigationContext> context2 =
  114. CreateContext(/*committed=*/true, /*is_same_document=*/false);
  115. bool blocking_page2_destroyed = false;
  116. CreateAssociatedBlockingPage(context2.get(), &blocking_page2_destroyed);
  117. EXPECT_FALSE(blocking_page1_destroyed);
  118. EXPECT_FALSE(blocking_page2_destroyed);
  119. // Associate the third interstitial.
  120. std::unique_ptr<web::NavigationContext> context3 =
  121. CreateContext(/*committed=*/true, /*is_same_document=*/false);
  122. bool blocking_page3_destroyed = false;
  123. CreateAssociatedBlockingPage(context3.get(), &blocking_page3_destroyed);
  124. EXPECT_FALSE(blocking_page1_destroyed);
  125. EXPECT_FALSE(blocking_page2_destroyed);
  126. EXPECT_FALSE(blocking_page3_destroyed);
  127. // Simulate committing the third interstitial.
  128. web_state_.OnNavigationFinished(context3.get());
  129. EXPECT_TRUE(blocking_page1_destroyed);
  130. EXPECT_FALSE(blocking_page2_destroyed);
  131. EXPECT_FALSE(blocking_page3_destroyed);
  132. // Simulate committing the second interstitial.
  133. web_state_.OnNavigationFinished(context2.get());
  134. EXPECT_TRUE(blocking_page1_destroyed);
  135. EXPECT_FALSE(blocking_page2_destroyed);
  136. EXPECT_TRUE(blocking_page3_destroyed);
  137. // Test that a subsequent committed navigation releases the last blocking
  138. // page.
  139. std::unique_ptr<web::NavigationContext> committed_context4 =
  140. CreateContext(/*committed=*/true, /*is_same_document=*/false);
  141. web_state_.OnNavigationFinished(committed_context4.get());
  142. EXPECT_TRUE(blocking_page2_destroyed);
  143. }
  144. // Tests that the helper properly handles a navigation that finishes without
  145. // committing.
  146. TEST_F(IOSBlockingPageTabHelperTest, NavigationDoesNotCommit) {
  147. std::unique_ptr<web::NavigationContext> committed_context =
  148. CreateContext(/*committed=*/true, /*is_same_document=*/false);
  149. bool committed_blocking_page_destroyed = false;
  150. CreateAssociatedBlockingPage(committed_context.get(),
  151. &committed_blocking_page_destroyed);
  152. web_state_.OnNavigationFinished(committed_context.get());
  153. EXPECT_FALSE(committed_blocking_page_destroyed);
  154. // Simulate a navigation that does not commit.
  155. std::unique_ptr<web::NavigationContext> non_committed_context =
  156. CreateContext(/*committed=*/false, /*is_same_document=*/false);
  157. bool non_committed_blocking_page_destroyed = false;
  158. CreateAssociatedBlockingPage(non_committed_context.get(),
  159. &non_committed_blocking_page_destroyed);
  160. web_state_.OnNavigationFinished(non_committed_context.get());
  161. // The blocking page for the non-committed navigation should have been cleaned
  162. // up, but the one for the previous committed navigation should still be
  163. // around.
  164. EXPECT_TRUE(non_committed_blocking_page_destroyed);
  165. EXPECT_FALSE(committed_blocking_page_destroyed);
  166. // When a navigation does commit, the previous one should be cleaned up.
  167. std::unique_ptr<web::NavigationContext> next_committed_context =
  168. CreateContext(/*committed=*/true, /*is_same_document=*/false);
  169. web_state_.OnNavigationFinished(next_committed_context.get());
  170. EXPECT_TRUE(committed_blocking_page_destroyed);
  171. }
  172. // Tests that a blocking page that is associated with a navigation ID after the
  173. // navigation is committed is correctly used as the current blocking page for
  174. // the last commited navigation ID.
  175. TEST_F(IOSBlockingPageTabHelperTest, BlockingPageAssociatedAfterCommit) {
  176. // Commit the navigation, then associate the blocking page.
  177. std::unique_ptr<web::NavigationContext> context =
  178. CreateContext(/*committed=*/true, /*is_same_document=*/false);
  179. web_state_.OnNavigationFinished(context.get());
  180. IOSSecurityInterstitialPage* page =
  181. CreateAssociatedBlockingPage(context.get(), nullptr);
  182. // Verify that the blocking page is used as the current page.
  183. EXPECT_EQ(page, helper()->GetCurrentBlockingPage());
  184. }
  185. } // namespace security_interstitials