1// Copyright 2014 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/prefs/pref_service.h"
7#include "base/strings/utf_string_conversions.h"
8#include "base/values.h"
9#include "chrome/browser/chrome_notification_types.h"
10#include "chrome/browser/history/history_service_factory.h"
11#include "chrome/browser/infobars/infobar_service.h"
12#include "chrome/browser/profiles/profile.h"
13#include "chrome/browser/supervised_user/supervised_user_constants.h"
14#include "chrome/browser/supervised_user/supervised_user_interstitial.h"
15#include "chrome/browser/supervised_user/supervised_user_navigation_observer.h"
16#include "chrome/browser/supervised_user/supervised_user_service.h"
17#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
18#include "chrome/browser/supervised_user/supervised_user_settings_service.h"
19#include "chrome/browser/supervised_user/supervised_user_settings_service_factory.h"
20#include "chrome/browser/ui/browser.h"
21#include "chrome/browser/ui/browser_navigator.h"
22#include "chrome/browser/ui/tabs/tab_strip_model.h"
23#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
24#include "chrome/common/chrome_switches.h"
25#include "chrome/common/pref_names.h"
26#include "chrome/test/base/in_process_browser_test.h"
27#include "chrome/test/base/ui_test_utils.h"
28#include "components/infobars/core/confirm_infobar_delegate.h"
29#include "components/infobars/core/infobar.h"
30#include "components/pref_registry/pref_registry_syncable.h"
31#include "content/public/browser/interstitial_page.h"
32#include "content/public/browser/navigation_controller.h"
33#include "content/public/browser/navigation_entry.h"
34#include "content/public/browser/notification_service.h"
35#include "content/public/browser/web_contents.h"
36#include "content/public/browser/web_contents_observer.h"
37#include "content/public/test/browser_test_utils.h"
38#include "testing/gmock/include/gmock/gmock.h"
39
40using content::InterstitialPage;
41using content::NavigationController;
42using content::NavigationEntry;
43using content::WebContents;
44
45namespace {
46
47// Tests the filter mode in which all sites are blocked by default.
48class SupervisedUserBlockModeTest : public InProcessBrowserTest {
49 public:
50  // Indicates whether the interstitial should proceed or not.
51  enum InterstitialAction {
52    INTERSTITIAL_PROCEED,
53    INTERSTITIAL_DONTPROCEED,
54  };
55
56  SupervisedUserBlockModeTest() : supervised_user_service_(NULL) {}
57  virtual ~SupervisedUserBlockModeTest() {}
58
59  void CheckShownPageIsInterstitial(WebContents* tab) {
60    CheckShownPage(tab, content::PAGE_TYPE_INTERSTITIAL);
61  }
62
63  void CheckShownPageIsNotInterstitial(WebContents* tab) {
64    CheckShownPage(tab, content::PAGE_TYPE_NORMAL);
65  }
66
67  // Checks to see if the type of the current page is |page_type|.
68  void CheckShownPage(WebContents* tab, content::PageType page_type) {
69    ASSERT_FALSE(tab->IsCrashed());
70    NavigationEntry* entry = tab->GetController().GetActiveEntry();
71    ASSERT_TRUE(entry);
72    ASSERT_EQ(page_type, entry->GetPageType());
73  }
74
75  void SendAccessRequest(WebContents* tab) {
76    InterstitialPage* interstitial_page = tab->GetInterstitialPage();
77    ASSERT_TRUE(interstitial_page);
78
79    // Get the SupervisedUserInterstitial delegate.
80    content::InterstitialPageDelegate* delegate =
81        interstitial_page->GetDelegateForTesting();
82
83    // Simulate the click on the "request" button.
84    delegate->CommandReceived("\"request\"");
85  }
86
87  void GoBack(WebContents* tab) {
88    InterstitialPage* interstitial_page = tab->GetInterstitialPage();
89    ASSERT_TRUE(interstitial_page);
90
91    // Get the SupervisedUserInterstitial delegate.
92    content::InterstitialPageDelegate* delegate =
93        interstitial_page->GetDelegateForTesting();
94
95    // Simulate the click on the "back" button.
96    delegate->CommandReceived("\"back\"");
97  }
98
99 protected:
100  virtual void SetUpOnMainThread() OVERRIDE {
101    // Set up the SupervisedUserNavigationObserver manually since the profile
102    // was not supervised when the browser was created.
103    content::WebContents* web_contents =
104        browser()->tab_strip_model()->GetActiveWebContents();
105    SupervisedUserNavigationObserver::CreateForWebContents(web_contents);
106
107    Profile* profile = browser()->profile();
108    supervised_user_service_ =
109        SupervisedUserServiceFactory::GetForProfile(profile);
110    SupervisedUserSettingsService* supervised_user_settings_service =
111        SupervisedUserSettingsServiceFactory::GetForProfile(profile);
112    supervised_user_settings_service->SetLocalSettingForTesting(
113        supervised_users::kContentPackDefaultFilteringBehavior,
114        scoped_ptr<base::Value>(
115            new base::FundamentalValue(SupervisedUserURLFilter::BLOCK)));
116  }
117
118  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
119    // Enable the test server and remap all URLs to it.
120    ASSERT_TRUE(test_server()->Start());
121    std::string host_port = test_server()->host_port_pair().ToString();
122    command_line->AppendSwitchASCII(switches::kHostResolverRules,
123        "MAP *.example.com " + host_port + "," +
124        "MAP *.new-example.com " + host_port + "," +
125        "MAP *.a.com " + host_port);
126
127    command_line->AppendSwitchASCII(switches::kSupervisedUserId, "asdf");
128  }
129
130  // Acts like a synchronous call to history's QueryHistory. Modified from
131  // history_querying_unittest.cc.
132  void QueryHistory(HistoryService* history_service,
133                    const std::string& text_query,
134                    const history::QueryOptions& options,
135                    history::QueryResults* results) {
136    base::RunLoop run_loop;
137    base::CancelableTaskTracker history_task_tracker;
138    history_service->QueryHistory(
139        base::UTF8ToUTF16(text_query),
140        options,
141        base::Bind(&SupervisedUserBlockModeTest::QueryHistoryComplete,
142                   base::Unretained(this),
143                   results,
144                   &run_loop),
145        &history_task_tracker);
146    run_loop.Run();  // Will go until ...Complete calls Quit.
147  }
148
149  void QueryHistoryComplete(history::QueryResults* new_results,
150                            base::RunLoop* run_loop,
151                            history::QueryResults* results) {
152    results->Swap(new_results);
153    run_loop->Quit();  // Will return out to QueryHistory.
154  }
155
156  SupervisedUserService* supervised_user_service_;
157};
158
159class MockTabStripModelObserver : public TabStripModelObserver {
160 public:
161  explicit MockTabStripModelObserver(TabStripModel* tab_strip)
162      : tab_strip_(tab_strip) {
163    tab_strip_->AddObserver(this);
164  }
165
166  ~MockTabStripModelObserver() {
167    tab_strip_->RemoveObserver(this);
168  }
169
170  MOCK_METHOD3(TabClosingAt, void(TabStripModel*, content::WebContents*, int));
171
172 private:
173  TabStripModel* tab_strip_;
174};
175
176// Navigates to a blocked URL.
177IN_PROC_BROWSER_TEST_F(SupervisedUserBlockModeTest,
178                       SendAccessRequestOnBlockedURL) {
179  GURL test_url("http://www.example.com/files/simple.html");
180  ui_test_utils::NavigateToURL(browser(), test_url);
181
182  WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
183
184  CheckShownPageIsInterstitial(tab);
185
186  SendAccessRequest(tab);
187
188  // TODO(sergiu): Properly check that the access request was sent here.
189
190  GoBack(tab);
191
192  // Make sure that the tab is still there.
193  EXPECT_EQ(tab, browser()->tab_strip_model()->GetActiveWebContents());
194
195  CheckShownPageIsNotInterstitial(tab);
196}
197
198// Navigates to a blocked URL in a new tab. We expect the tab to be closed
199// automatically on pressing the "back" button on the interstitial.
200IN_PROC_BROWSER_TEST_F(SupervisedUserBlockModeTest, OpenBlockedURLInNewTab) {
201  TabStripModel* tab_strip = browser()->tab_strip_model();
202  WebContents* prev_tab = tab_strip->GetActiveWebContents();
203
204  // Open blocked URL in a new tab.
205  GURL test_url("http://www.example.com/files/simple.html");
206  ui_test_utils::NavigateToURLWithDisposition(
207      browser(), test_url, NEW_FOREGROUND_TAB,
208      ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
209
210  // Check that we got the interstitial.
211  WebContents* tab = tab_strip->GetActiveWebContents();
212  CheckShownPageIsInterstitial(tab);
213
214  // On pressing the "back" button, the new tab should be closed, and we should
215  // get back to the previous active tab.
216  MockTabStripModelObserver observer(tab_strip);
217  base::RunLoop run_loop;
218  EXPECT_CALL(observer,
219              TabClosingAt(tab_strip, tab, tab_strip->active_index()))
220      .WillOnce(testing::InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
221  GoBack(tab);
222  run_loop.Run();
223  EXPECT_EQ(prev_tab, tab_strip->GetActiveWebContents());
224}
225
226// Tests whether a visit attempt adds a special history entry.
227IN_PROC_BROWSER_TEST_F(SupervisedUserBlockModeTest,
228                       HistoryVisitRecorded) {
229  GURL allowed_url("http://www.example.com/files/simple.html");
230
231  // Set the host as allowed.
232  scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
233  dict->SetBooleanWithoutPathExpansion(allowed_url.host(), true);
234  SupervisedUserSettingsService* supervised_user_settings_service =
235      SupervisedUserSettingsServiceFactory::GetForProfile(
236          browser()->profile());
237  supervised_user_settings_service->SetLocalSettingForTesting(
238      supervised_users::kContentPackManualBehaviorHosts,
239      dict.PassAs<base::Value>());
240  EXPECT_EQ(
241      SupervisedUserService::MANUAL_ALLOW,
242      supervised_user_service_->GetManualBehaviorForHost(allowed_url.host()));
243
244  ui_test_utils::NavigateToURL(browser(), allowed_url);
245
246  // Navigate to it and check that we don't get an interstitial.
247  WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
248  CheckShownPageIsNotInterstitial(tab);
249
250  // Navigate to a blocked page and go back on the interstitial.
251  GURL blocked_url("http://www.new-example.com/files/simple.html");
252  ui_test_utils::NavigateToURL(browser(), blocked_url);
253
254  tab = browser()->tab_strip_model()->GetActiveWebContents();
255
256  CheckShownPageIsInterstitial(tab);
257  GoBack(tab);
258
259  // Check that we went back to the first URL and that the manual behaviors
260  // have not changed.
261  EXPECT_EQ(allowed_url.spec(), tab->GetURL().spec());
262  EXPECT_EQ(SupervisedUserService::MANUAL_ALLOW,
263            supervised_user_service_->GetManualBehaviorForHost(
264                "www.example.com"));
265  EXPECT_EQ(
266      SupervisedUserService::MANUAL_NONE,
267      supervised_user_service_->GetManualBehaviorForHost(
268          "www.new-example.com"));
269
270  // Query the history entry.
271  HistoryService* history_service = HistoryServiceFactory::GetForProfile(
272      browser()->profile(), Profile::EXPLICIT_ACCESS);
273  history::QueryOptions options;
274  history::QueryResults results;
275  QueryHistory(history_service, "", options, &results);
276
277  // Check that the entries have the correct blocked_visit value.
278  ASSERT_EQ(2u, results.size());
279  EXPECT_EQ(blocked_url.spec(), results[0].url().spec());
280  EXPECT_TRUE(results[0].blocked_visit());
281  EXPECT_EQ(allowed_url.spec(), results[1].url().spec());
282  EXPECT_FALSE(results[1].blocked_visit());
283}
284
285IN_PROC_BROWSER_TEST_F(SupervisedUserBlockModeTest, Unblock) {
286  GURL test_url("http://www.example.com/files/simple.html");
287  ui_test_utils::NavigateToURL(browser(), test_url);
288
289  WebContents* web_contents =
290      browser()->tab_strip_model()->GetActiveWebContents();
291
292  CheckShownPageIsInterstitial(web_contents);
293
294  content::WindowedNotificationObserver observer(
295      content::NOTIFICATION_LOAD_STOP,
296      content::NotificationService::AllSources());
297
298  // Set the host as allowed.
299  scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
300  dict->SetBooleanWithoutPathExpansion(test_url.host(), true);
301  SupervisedUserSettingsService* supervised_user_settings_service =
302      SupervisedUserSettingsServiceFactory::GetForProfile(
303          browser()->profile());
304  supervised_user_settings_service->SetLocalSettingForTesting(
305      supervised_users::kContentPackManualBehaviorHosts,
306      dict.PassAs<base::Value>());
307  EXPECT_EQ(
308      SupervisedUserService::MANUAL_ALLOW,
309      supervised_user_service_->GetManualBehaviorForHost(test_url.host()));
310
311  observer.Wait();
312  EXPECT_EQ(test_url, web_contents->GetURL());
313}
314
315}  // namespace
316