1// Copyright 2017 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
5#import "ios/chrome/browser/web/blocked_popup_tab_helper.h"
6
7#include "base/memory/ptr_util.h"
8#include "base/test/metrics/histogram_tester.h"
9#include "components/content_settings/core/browser/host_content_settings_map.h"
10#include "components/infobars/core/confirm_infobar_delegate.h"
11#include "components/infobars/core/infobar.h"
12#include "components/infobars/core/infobar_manager.h"
13#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
14#include "ios/chrome/browser/content_settings/host_content_settings_map_factory.h"
15#include "ios/chrome/browser/infobars/confirm_infobar_metrics_recorder.h"
16#include "ios/chrome/browser/infobars/infobar_manager_impl.h"
17#import "ios/chrome/browser/web/chrome_web_test.h"
18#import "ios/web/public/test/fakes/test_navigation_manager.h"
19#import "ios/web/public/test/fakes/test_web_state.h"
20#import "ios/web/public/test/fakes/test_web_state_delegate.h"
21#include "testing/gtest/include/gtest/gtest.h"
22#include "url/gurl.h"
23
24#if !defined(__has_feature) || !__has_feature(objc_arc)
25#error "This file requires ARC support."
26#endif
27
28using web::WebState;
29
30// Test fixture for BlockedPopupTabHelper class.
31class BlockedPopupTabHelperTest : public ChromeWebTest {
32 protected:
33  void SetUp() override {
34    ChromeWebTest::SetUp();
35    web_state()->SetDelegate(&web_state_delegate_);
36    BlockedPopupTabHelper::CreateForWebState(web_state());
37    InfoBarManagerImpl::CreateForWebState(web_state());
38  }
39
40  // Returns true if InfoBarManager is being observed.
41  bool IsObservingSources() {
42    return GetBlockedPopupTabHelper()->scoped_observer_.IsObservingSources();
43  }
44
45  // Returns BlockedPopupTabHelper that is being tested.
46  BlockedPopupTabHelper* GetBlockedPopupTabHelper() {
47    return BlockedPopupTabHelper::FromWebState(web_state());
48  }
49
50  // Returns InfoBarManager attached to |web_state()|.
51  infobars::InfoBarManager* GetInfobarManager() {
52    return InfoBarManagerImpl::FromWebState(web_state());
53  }
54
55  web::TestWebStateDelegate web_state_delegate_;
56};
57
58// Tests ShouldBlockPopup method. This test changes content settings without
59// restoring them back, which is fine because changes do not persist across test
60// runs.
61TEST_F(BlockedPopupTabHelperTest, ShouldBlockPopup) {
62  const GURL source_url1("https://source-url1");
63  EXPECT_TRUE(GetBlockedPopupTabHelper()->ShouldBlockPopup(source_url1));
64
65  // Allow popups for |source_url1|.
66  scoped_refptr<HostContentSettingsMap> settings_map(
67      ios::HostContentSettingsMapFactory::GetForBrowserState(
68          chrome_browser_state_.get()));
69  settings_map->SetContentSettingCustomScope(
70      ContentSettingsPattern::FromURL(source_url1),
71      ContentSettingsPattern::Wildcard(), ContentSettingsType::POPUPS,
72      CONTENT_SETTING_ALLOW);
73
74  EXPECT_FALSE(GetBlockedPopupTabHelper()->ShouldBlockPopup(source_url1));
75  const GURL source_url2("https://source-url2");
76  EXPECT_TRUE(GetBlockedPopupTabHelper()->ShouldBlockPopup(source_url2));
77
78  // Allow all popups.
79  settings_map->SetDefaultContentSetting(ContentSettingsType::POPUPS,
80                                         CONTENT_SETTING_ALLOW);
81
82  EXPECT_FALSE(GetBlockedPopupTabHelper()->ShouldBlockPopup(source_url1));
83  EXPECT_FALSE(GetBlockedPopupTabHelper()->ShouldBlockPopup(source_url2));
84}
85
86// Tests that allowing blocked popup opens a child window and allows future
87// popups for the source url.
88TEST_F(BlockedPopupTabHelperTest, AllowBlockedPopup) {
89  const GURL source_url("https://source-url");
90  ASSERT_TRUE(GetBlockedPopupTabHelper()->ShouldBlockPopup(source_url));
91
92  // Block popup.
93  const GURL target_url("https://target-url");
94  web::Referrer referrer(source_url, web::ReferrerPolicyDefault);
95  GetBlockedPopupTabHelper()->HandlePopup(target_url, referrer);
96
97  // Allow blocked popup.
98  ASSERT_EQ(1U, GetInfobarManager()->infobar_count());
99  infobars::InfoBar* infobar = GetInfobarManager()->infobar_at(0);
100  auto* delegate = infobar->delegate()->AsConfirmInfoBarDelegate();
101  ASSERT_TRUE(delegate);
102  ASSERT_FALSE(web_state_delegate_.last_open_url_request());
103  delegate->Accept();
104
105  // Verify that popups are allowed for |test_url|.
106  EXPECT_FALSE(GetBlockedPopupTabHelper()->ShouldBlockPopup(source_url));
107
108  // Verify that child window was open.
109  auto* open_url_request = web_state_delegate_.last_open_url_request();
110  ASSERT_TRUE(open_url_request);
111  EXPECT_EQ(web_state(), open_url_request->web_state);
112  WebState::OpenURLParams params = open_url_request->params;
113  EXPECT_EQ(target_url, params.url);
114  EXPECT_EQ(source_url, params.referrer.url);
115  EXPECT_EQ(web::ReferrerPolicyDefault, params.referrer.policy);
116  EXPECT_EQ(WindowOpenDisposition::NEW_POPUP, params.disposition);
117  EXPECT_TRUE(
118      PageTransitionCoreTypeIs(params.transition, ui::PAGE_TRANSITION_LINK));
119  EXPECT_TRUE(params.is_renderer_initiated);
120}
121
122// Tests that destroying WebState while Infobar is presented does not crash.
123TEST_F(BlockedPopupTabHelperTest, DestroyWebState) {
124  const GURL source_url("https://source-url");
125  ASSERT_TRUE(GetBlockedPopupTabHelper()->ShouldBlockPopup(source_url));
126
127  // Block popup.
128  const GURL target_url("https://target-url");
129  web::Referrer referrer(source_url, web::ReferrerPolicyDefault);
130  GetBlockedPopupTabHelper()->HandlePopup(target_url, referrer);
131
132  // Verify that destroying WebState does not crash.
133  DestroyWebState();
134}
135
136// Tests that an infobar is added to the infobar manager when
137// BlockedPopupTabHelper::HandlePopup() is called.
138TEST_F(BlockedPopupTabHelperTest, ShowAndDismissInfoBar) {
139  // Check that there are no infobars showing and no registered observers.
140  EXPECT_EQ(0U, GetInfobarManager()->infobar_count());
141  EXPECT_FALSE(IsObservingSources());
142
143  // Call |HandlePopup| to show an infobar.
144  const GURL test_url("https://popups.example.com");
145  GetBlockedPopupTabHelper()->HandlePopup(test_url, web::Referrer());
146  ASSERT_EQ(1U, GetInfobarManager()->infobar_count());
147  EXPECT_TRUE(IsObservingSources());
148
149  // Dismiss the infobar and check that the tab helper no longer has any
150  // registered observers.
151  GetInfobarManager()->infobar_at(0)->RemoveSelf();
152  EXPECT_EQ(0U, GetInfobarManager()->infobar_count());
153  EXPECT_FALSE(IsObservingSources());
154}
155
156// Tests that the Infobar presentation and dismissal histograms are recorded
157// correctly.
158TEST_F(BlockedPopupTabHelperTest, RecordDismissMetrics) {
159  base::HistogramTester histogram_tester;
160
161  // Call |HandlePopup| to show an infobar and check that the Presented
162  // histogram was recorded correctly.
163  const GURL test_url("https://popups.example.com");
164  GetBlockedPopupTabHelper()->HandlePopup(test_url, web::Referrer());
165  ASSERT_EQ(1U, GetInfobarManager()->infobar_count());
166  histogram_tester.ExpectUniqueSample(
167      "Mobile.Messages.Confirm.Event.ConfirmInfobarTypeBlockPopups",
168      static_cast<base::HistogramBase::Sample>(
169          MobileMessagesConfirmInfobarEvents::Presented),
170      1);
171
172  // Dismiss the infobar and check that the Dismiss histogram was recorded
173  // correctly.
174  GetInfobarManager()->infobar_at(0)->delegate()->InfoBarDismissed();
175  histogram_tester.ExpectBucketCount(
176      kInfobarTypeBlockPopupsEventHistogram,
177      static_cast<base::HistogramBase::Sample>(
178          MobileMessagesConfirmInfobarEvents::Dismissed),
179      1);
180}
181
182// Tests that the Infobar accept histogram is recorded correctly.
183TEST_F(BlockedPopupTabHelperTest, RecordAcceptMetrics) {
184  base::HistogramTester histogram_tester;
185  const GURL source_url("https://source-url");
186  ASSERT_TRUE(GetBlockedPopupTabHelper()->ShouldBlockPopup(source_url));
187
188  // Block popup.
189  const GURL target_url("https://target-url");
190  web::Referrer referrer(source_url, web::ReferrerPolicyDefault);
191  GetBlockedPopupTabHelper()->HandlePopup(target_url, referrer);
192
193  // Accept the infobar and check that the Accepted histogram was recorded
194  // correctly.
195  ASSERT_EQ(1U, GetInfobarManager()->infobar_count());
196  auto* delegate = GetInfobarManager()
197                       ->infobar_at(0)
198                       ->delegate()
199                       ->AsConfirmInfoBarDelegate();
200  delegate->Accept();
201  histogram_tester.ExpectBucketCount(
202      kInfobarTypeBlockPopupsEventHistogram,
203      static_cast<base::HistogramBase::Sample>(
204          MobileMessagesConfirmInfobarEvents::Accepted),
205      1);
206}
207