1// Copyright 2013 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#include "base/command_line.h"
6#include "base/files/file_path.h"
7#include "base/message_loop/message_loop.h"
8#include "base/path_service.h"
9#include "base/run_loop.h"
10#include "base/strings/utf_string_conversions.h"
11#include "chrome/browser/chrome_notification_types.h"
12#include "chrome/browser/content_settings/host_content_settings_map.h"
13#include "chrome/browser/content_settings/tab_specific_content_settings.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/browser/search_engines/template_url_service_factory.h"
16#include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h"
17#include "chrome/browser/ui/browser.h"
18#include "chrome/browser/ui/browser_commands.h"
19#include "chrome/browser/ui/browser_finder.h"
20#include "chrome/browser/ui/browser_window.h"
21#include "chrome/browser/ui/location_bar/location_bar.h"
22#include "chrome/browser/ui/omnibox/omnibox_edit_model.h"
23#include "chrome/browser/ui/omnibox/omnibox_view.h"
24#include "chrome/browser/ui/tabs/tab_strip_model.h"
25#include "chrome/common/chrome_paths.h"
26#include "chrome/common/chrome_switches.h"
27#include "chrome/test/base/in_process_browser_test.h"
28#include "chrome/test/base/test_switches.h"
29#include "chrome/test/base/ui_test_utils.h"
30#include "components/omnibox/autocomplete_match.h"
31#include "components/omnibox/autocomplete_result.h"
32#include "content/public/browser/notification_registrar.h"
33#include "content/public/browser/notification_service.h"
34#include "content/public/browser/render_frame_host.h"
35#include "content/public/browser/web_contents.h"
36#include "content/public/browser/web_contents_observer.h"
37#include "content/public/common/url_constants.h"
38#include "content/public/test/browser_test_utils.h"
39#include "content/public/test/test_navigation_observer.h"
40#include "net/dns/mock_host_resolver.h"
41#include "net/test/embedded_test_server/embedded_test_server.h"
42#include "testing/gtest/include/gtest/gtest.h"
43
44using content::WebContents;
45
46namespace {
47
48// Counts the number of RenderViewHosts created.
49class CountRenderViewHosts : public content::NotificationObserver {
50 public:
51  CountRenderViewHosts()
52      : count_(0) {
53    registrar_.Add(this,
54                   content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
55                   content::NotificationService::AllSources());
56  }
57  virtual ~CountRenderViewHosts() {}
58
59  int GetRenderViewHostCreatedCount() const { return count_; }
60
61 private:
62  virtual void Observe(int type,
63                       const content::NotificationSource& source,
64                       const content::NotificationDetails& details) OVERRIDE {
65    count_++;
66  }
67
68  content::NotificationRegistrar registrar_;
69
70  int count_;
71
72  DISALLOW_COPY_AND_ASSIGN(CountRenderViewHosts);
73};
74
75class CloseObserver : public content::WebContentsObserver {
76 public:
77  explicit CloseObserver(WebContents* contents)
78      : content::WebContentsObserver(contents) {}
79
80  void Wait() {
81    close_loop_.Run();
82  }
83
84  virtual void WebContentsDestroyed() OVERRIDE {
85    close_loop_.Quit();
86  }
87
88 private:
89  base::RunLoop close_loop_;
90
91  DISALLOW_COPY_AND_ASSIGN(CloseObserver);
92};
93
94class PopupBlockerBrowserTest : public InProcessBrowserTest {
95 public:
96  PopupBlockerBrowserTest() {}
97  virtual ~PopupBlockerBrowserTest() {}
98
99  virtual void SetUpOnMainThread() OVERRIDE {
100    InProcessBrowserTest::SetUpOnMainThread();
101
102    host_resolver()->AddRule("*", "127.0.0.1");
103    ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
104  }
105
106  int GetBlockedContentsCount() {
107    // Do a round trip to the renderer first to flush any in-flight IPCs to
108    // create a to-be-blocked window.
109    WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
110    CHECK(content::ExecuteScript(tab, std::string()));
111    PopupBlockerTabHelper* popup_blocker_helper =
112        PopupBlockerTabHelper::FromWebContents(tab);
113    return popup_blocker_helper->GetBlockedPopupsCount();
114  }
115
116  enum WhatToExpect {
117    ExpectPopup,
118    ExpectTab
119  };
120
121  enum ShouldCheckTitle {
122    CheckTitle,
123    DontCheckTitle
124  };
125
126  void NavigateAndCheckPopupShown(const GURL& url,
127                                  WhatToExpect what_to_expect) {
128    content::WindowedNotificationObserver observer(
129        chrome::NOTIFICATION_TAB_ADDED,
130        content::NotificationService::AllSources());
131    ui_test_utils::NavigateToURL(browser(), url);
132    observer.Wait();
133
134    if (what_to_expect == ExpectPopup) {
135      ASSERT_EQ(2u,
136                chrome::GetBrowserCount(browser()->profile(),
137                                        browser()->host_desktop_type()));
138    } else {
139      ASSERT_EQ(1u,
140                chrome::GetBrowserCount(browser()->profile(),
141                                        browser()->host_desktop_type()));
142      ASSERT_EQ(2, browser()->tab_strip_model()->count());
143    }
144
145    ASSERT_EQ(0, GetBlockedContentsCount());
146  }
147
148  // Navigates to the test indicated by |test_name| using |browser| which is
149  // expected to try to open a popup. Verifies that the popup was blocked and
150  // then opens the blocked popup. Once the popup stopped loading, verifies
151  // that the title of the page is "PASS" if |check_title| is set.
152  //
153  // If |what_to_expect| is ExpectPopup, the popup is expected to open a new
154  // window, or a background tab if it is false.
155  //
156  // Returns the WebContents of the launched popup.
157  WebContents* RunCheckTest(Browser* browser,
158                            const std::string& test_name,
159                            WhatToExpect what_to_expect,
160                            ShouldCheckTitle check_title) {
161    GURL url(embedded_test_server()->GetURL(test_name));
162
163    CountRenderViewHosts counter;
164
165    ui_test_utils::NavigateToURL(browser, url);
166
167    // Since the popup blocker blocked the window.open, there should be only one
168    // tab.
169    EXPECT_EQ(1u, chrome::GetBrowserCount(browser->profile(),
170                                          browser->host_desktop_type()));
171    EXPECT_EQ(1, browser->tab_strip_model()->count());
172    WebContents* web_contents =
173        browser->tab_strip_model()->GetActiveWebContents();
174    EXPECT_EQ(url, web_contents->GetURL());
175
176    // And no new RVH created.
177    EXPECT_EQ(0, counter.GetRenderViewHostCreatedCount());
178
179    content::WindowedNotificationObserver observer(
180        chrome::NOTIFICATION_TAB_ADDED,
181        content::NotificationService::AllSources());
182    ui_test_utils::BrowserAddedObserver browser_observer;
183
184    // Launch the blocked popup.
185    PopupBlockerTabHelper* popup_blocker_helper =
186        PopupBlockerTabHelper::FromWebContents(web_contents);
187    if (!popup_blocker_helper->GetBlockedPopupsCount()) {
188      content::WindowedNotificationObserver observer(
189          chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED,
190          content::NotificationService::AllSources());
191      observer.Wait();
192    }
193    EXPECT_EQ(1u, popup_blocker_helper->GetBlockedPopupsCount());
194    std::map<int32, GURL> blocked_requests =
195        popup_blocker_helper->GetBlockedPopupRequests();
196    std::map<int32, GURL>::const_iterator iter = blocked_requests.begin();
197    popup_blocker_helper->ShowBlockedPopup(iter->first);
198
199    observer.Wait();
200    Browser* new_browser;
201    if (what_to_expect == ExpectPopup) {
202      new_browser = browser_observer.WaitForSingleNewBrowser();
203      web_contents = new_browser->tab_strip_model()->GetActiveWebContents();
204    } else {
205      new_browser = browser;
206      EXPECT_EQ(2, browser->tab_strip_model()->count());
207      web_contents = browser->tab_strip_model()->GetWebContentsAt(1);
208    }
209
210    if (check_title == CheckTitle) {
211      // Check that the check passed.
212      base::string16 expected_title(base::ASCIIToUTF16("PASS"));
213      content::TitleWatcher title_watcher(web_contents, expected_title);
214      EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
215    }
216
217    return web_contents;
218  }
219
220 private:
221  DISALLOW_COPY_AND_ASSIGN(PopupBlockerBrowserTest);
222};
223
224IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest,
225                       BlockWebContentsCreation) {
226#if defined(OS_WIN) && defined(USE_ASH)
227  // Disable this test in Metro+Ash for now (http://crbug.com/262796).
228  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
229    return;
230#endif
231
232  RunCheckTest(
233      browser(),
234      "/popup_blocker/popup-blocked-to-post-blank.html",
235      ExpectPopup,
236      DontCheckTitle);
237}
238
239IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest,
240                       BlockWebContentsCreationIncognito) {
241#if defined(OS_WIN) && defined(USE_ASH)
242  // Disable this test in Metro+Ash for now (http://crbug.com/262796).
243  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
244    return;
245#endif
246
247  RunCheckTest(
248      CreateIncognitoBrowser(),
249      "/popup_blocker/popup-blocked-to-post-blank.html",
250      ExpectPopup,
251      DontCheckTitle);
252}
253
254IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest,
255                       PopupBlockedFakeClickOnAnchor) {
256#if defined(OS_WIN) && defined(USE_ASH)
257  // Disable this test in Metro+Ash for now (http://crbug.com/262796).
258  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
259    return;
260#endif
261
262  RunCheckTest(
263      browser(),
264      "/popup_blocker/popup-fake-click-on-anchor.html",
265      ExpectTab,
266      DontCheckTitle);
267}
268
269IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest,
270                       PopupBlockedFakeClickOnAnchorNoTarget) {
271#if defined(OS_WIN) && defined(USE_ASH)
272  // Disable this test in Metro+Ash for now (http://crbug.com/262796).
273  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
274    return;
275#endif
276
277  RunCheckTest(
278      browser(),
279      "/popup_blocker/popup-fake-click-on-anchor2.html",
280      ExpectTab,
281      DontCheckTitle);
282}
283
284IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest, MultiplePopups) {
285  GURL url(embedded_test_server()->GetURL("/popup_blocker/popup-many.html"));
286  ui_test_utils::NavigateToURL(browser(), url);
287  ASSERT_EQ(2, GetBlockedContentsCount());
288}
289
290// Verify that popups are launched on browser back button.
291IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest,
292                       AllowPopupThroughContentSetting) {
293  GURL url(embedded_test_server()->GetURL(
294      "/popup_blocker/popup-blocked-to-post-blank.html"));
295  browser()->profile()->GetHostContentSettingsMap()
296      ->SetContentSetting(ContentSettingsPattern::FromURL(url),
297                          ContentSettingsPattern::Wildcard(),
298                          CONTENT_SETTINGS_TYPE_POPUPS,
299                          std::string(),
300                          CONTENT_SETTING_ALLOW);
301
302  NavigateAndCheckPopupShown(url, ExpectTab);
303}
304
305// Verify that content settings are applied based on the top-level frame URL.
306IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest,
307                       AllowPopupThroughContentSettingIFrame) {
308  GURL url(embedded_test_server()->GetURL("/popup_blocker/popup-frames.html"));
309  browser()->profile()->GetHostContentSettingsMap()
310      ->SetContentSetting(ContentSettingsPattern::FromURL(url),
311                          ContentSettingsPattern::Wildcard(),
312                          CONTENT_SETTINGS_TYPE_POPUPS,
313                          std::string(),
314                          CONTENT_SETTING_ALLOW);
315
316  // Popup from the iframe should be allowed since the top-level URL is
317  // whitelisted.
318  NavigateAndCheckPopupShown(url, ExpectTab);
319
320  // Whitelist iframe URL instead.
321  GURL::Replacements replace_host;
322  std::string host_str("www.a.com");  // Must stay in scope with replace_host
323  replace_host.SetHostStr(host_str);
324  GURL frame_url(embedded_test_server()
325                     ->GetURL("/popup_blocker/popup-frames-iframe.html")
326                     .ReplaceComponents(replace_host));
327  browser()->profile()->GetHostContentSettingsMap()->ClearSettingsForOneType(
328      CONTENT_SETTINGS_TYPE_POPUPS);
329  browser()->profile()->GetHostContentSettingsMap()
330      ->SetContentSetting(ContentSettingsPattern::FromURL(frame_url),
331                          ContentSettingsPattern::Wildcard(),
332                          CONTENT_SETTINGS_TYPE_POPUPS,
333                          std::string(),
334                          CONTENT_SETTING_ALLOW);
335
336  // Popup should be blocked.
337  ui_test_utils::NavigateToURL(browser(), url);
338  ASSERT_EQ(1, GetBlockedContentsCount());
339}
340
341IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest,
342                       PopupsLaunchWhenTabIsClosed) {
343  CommandLine::ForCurrentProcess()->AppendSwitch(
344      switches::kDisablePopupBlocking);
345  GURL url(
346      embedded_test_server()->GetURL("/popup_blocker/popup-on-unload.html"));
347  ui_test_utils::NavigateToURL(browser(), url);
348
349  NavigateAndCheckPopupShown(embedded_test_server()->GetURL("/popup_blocker/"),
350                             ExpectPopup);
351}
352
353// Verify that when you unblock popup, the popup shows in history and omnibox.
354IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest,
355                       UnblockedPopupShowsInHistoryAndOmnibox) {
356  CommandLine::ForCurrentProcess()->AppendSwitch(
357      switches::kDisablePopupBlocking);
358  GURL url(embedded_test_server()->GetURL(
359      "/popup_blocker/popup-blocked-to-post-blank.html"));
360  NavigateAndCheckPopupShown(url, ExpectTab);
361
362  std::string search_string =
363      "data:text/html,<title>Popup Success!</title>you should not see this "
364      "message if popup blocker is enabled";
365
366  ui_test_utils::HistoryEnumerator history(browser()->profile());
367  std::vector<GURL>& history_urls = history.urls();
368  ASSERT_EQ(2u, history_urls.size());
369  ASSERT_EQ(GURL(search_string), history_urls[0]);
370  ASSERT_EQ(url, history_urls[1]);
371
372  TemplateURLService* service = TemplateURLServiceFactory::GetForProfile(
373      browser()->profile());
374  ui_test_utils::WaitForTemplateURLServiceToLoad(service);
375  LocationBar* location_bar = browser()->window()->GetLocationBar();
376  ui_test_utils::SendToOmniboxAndSubmit(location_bar, search_string);
377  OmniboxEditModel* model = location_bar->GetOmniboxView()->model();
378  EXPECT_EQ(GURL(search_string), model->CurrentMatch(NULL).destination_url);
379  EXPECT_EQ(base::ASCIIToUTF16(search_string),
380            model->CurrentMatch(NULL).contents);
381}
382
383// This test fails on linux AURA with this change
384// https://codereview.chromium.org/23903056
385// BUG=https://code.google.com/p/chromium/issues/detail?id=295299
386// TODO(ananta). Debug and fix this test.
387#if defined(USE_AURA) && defined(OS_LINUX)
388#define MAYBE_WindowFeatures DISABLED_WindowFeatures
389#else
390#define MAYBE_WindowFeatures WindowFeatures
391#endif
392IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest, MAYBE_WindowFeatures) {
393  WebContents* popup =
394      RunCheckTest(browser(),
395                   "/popup_blocker/popup-window-open.html",
396                   ExpectPopup,
397                   DontCheckTitle);
398
399  // Check that the new popup has (roughly) the requested size.
400  gfx::Size window_size = popup->GetContainerBounds().size();
401  EXPECT_TRUE(349 <= window_size.width() && window_size.width() <= 351);
402  EXPECT_TRUE(249 <= window_size.height() && window_size.height() <= 251);
403}
404
405IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest, CorrectReferrer) {
406  RunCheckTest(browser(),
407               "/popup_blocker/popup-referrer.html",
408               ExpectPopup,
409               CheckTitle);
410}
411
412IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest, WindowFeaturesBarProps) {
413  RunCheckTest(browser(),
414               "/popup_blocker/popup-windowfeatures.html",
415               ExpectPopup,
416               CheckTitle);
417}
418
419IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest, SessionStorage) {
420  RunCheckTest(browser(),
421               "/popup_blocker/popup-sessionstorage.html",
422               ExpectPopup,
423               CheckTitle);
424}
425
426IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest, Opener) {
427  RunCheckTest(browser(),
428               "/popup_blocker/popup-opener.html",
429               ExpectPopup,
430               CheckTitle);
431}
432
433// Tests that the popup can still close itself after navigating. This tests that
434// the openedByDOM bit is preserved across blocked popups.
435IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest, ClosableAfterNavigation) {
436  // Open a popup.
437  WebContents* popup =
438      RunCheckTest(browser(),
439                   "/popup_blocker/popup-opener.html",
440                   ExpectPopup,
441                   CheckTitle);
442
443  // Navigate it elsewhere.
444  content::TestNavigationObserver nav_observer(popup);
445  popup->GetMainFrame()->ExecuteJavaScript(
446      base::UTF8ToUTF16("location.href = '/empty.html'"));
447  nav_observer.Wait();
448
449  // Have it close itself.
450  CloseObserver close_observer(popup);
451  popup->GetMainFrame()->ExecuteJavaScript(
452      base::UTF8ToUTF16("window.close()"));
453  close_observer.Wait();
454}
455
456IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest, OpenerSuppressed) {
457  RunCheckTest(browser(),
458               "/popup_blocker/popup-openersuppressed.html",
459               ExpectTab,
460               CheckTitle);
461}
462
463IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest, ShiftClick) {
464  RunCheckTest(
465      browser(),
466      "/popup_blocker/popup-fake-click-on-anchor3.html",
467      ExpectPopup,
468      CheckTitle);
469}
470
471IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest, WebUI) {
472  WebContents* popup =
473      RunCheckTest(browser(),
474                   "/popup_blocker/popup-webui.html",
475                   ExpectPopup,
476                   DontCheckTitle);
477
478  // Check that the new popup displays about:blank.
479  EXPECT_EQ(GURL(url::kAboutBlankURL), popup->GetURL());
480}
481
482// Verify that the renderer can't DOS the browser by creating arbitrarily many
483// popups.
484IN_PROC_BROWSER_TEST_F(PopupBlockerBrowserTest, DenialOfService) {
485  GURL url(embedded_test_server()->GetURL("/popup_blocker/popup-dos.html"));
486  ui_test_utils::NavigateToURL(browser(), url);
487  ASSERT_EQ(25, GetBlockedContentsCount());
488}
489
490}  // namespace
491