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/prefs/pref_service.h"
7#include "base/run_loop.h"
8#include "base/strings/utf_string_conversions.h"
9#include "chrome/browser/net/prediction_options.h"
10#include "chrome/browser/profiles/profile.h"
11#include "chrome/browser/ui/browser.h"
12#include "chrome/browser/ui/tabs/tab_strip_model.h"
13#include "chrome/common/chrome_switches.h"
14#include "chrome/common/pref_names.h"
15#include "chrome/common/prefetch_messages.h"
16#include "chrome/test/base/in_process_browser_test.h"
17#include "chrome/test/base/ui_test_utils.h"
18#include "content/public/browser/render_frame_host.h"
19#include "content/public/browser/web_contents.h"
20#include "content/public/test/browser_test_utils.h"
21#include "net/base/network_change_notifier.h"
22#include "net/url_request/url_request_filter.h"
23#include "net/url_request/url_request_job.h"
24
25using chrome_browser_net::NetworkPredictionOptions;
26using content::BrowserThread;
27using net::NetworkChangeNotifier;
28
29namespace {
30
31const char kPrefetchPage[] = "files/prerender/simple_prefetch.html";
32
33class MockNetworkChangeNotifierWIFI : public NetworkChangeNotifier {
34 public:
35  virtual ConnectionType GetCurrentConnectionType() const OVERRIDE {
36    return NetworkChangeNotifier::CONNECTION_WIFI;
37  }
38};
39
40class MockNetworkChangeNotifier4G : public NetworkChangeNotifier {
41 public:
42  virtual ConnectionType GetCurrentConnectionType() const OVERRIDE {
43    return NetworkChangeNotifier::CONNECTION_4G;
44  }
45};
46
47class PrefetchBrowserTestBase : public InProcessBrowserTest {
48 public:
49  explicit PrefetchBrowserTestBase(bool disabled_via_field_trial)
50      : disabled_via_field_trial_(disabled_via_field_trial) {}
51
52  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
53    if (disabled_via_field_trial_) {
54      command_line->AppendSwitchASCII(switches::kForceFieldTrials,
55                                      "Prefetch/ExperimentDisabled/");
56    }
57  }
58
59  void SetPreference(NetworkPredictionOptions value) {
60    browser()->profile()->GetPrefs()->SetInteger(
61        prefs::kNetworkPredictionOptions, value);
62  }
63
64  bool RunPrefetchExperiment(bool expect_success, Browser* browser) {
65    GURL url = test_server()->GetURL(kPrefetchPage);
66
67    const base::string16 expected_title =
68        expect_success ? base::ASCIIToUTF16("link onload")
69                       : base::ASCIIToUTF16("link onerror");
70    content::TitleWatcher title_watcher(
71        browser->tab_strip_model()->GetActiveWebContents(), expected_title);
72    ui_test_utils::NavigateToURL(browser, url);
73    return expected_title == title_watcher.WaitAndGetTitle();
74  }
75
76 private:
77  bool disabled_via_field_trial_;
78};
79
80class PrefetchBrowserTestPrediction : public PrefetchBrowserTestBase {
81 public:
82  PrefetchBrowserTestPrediction() : PrefetchBrowserTestBase(false) {}
83};
84
85class PrefetchBrowserTestPredictionDisabled : public PrefetchBrowserTestBase {
86 public:
87  PrefetchBrowserTestPredictionDisabled() : PrefetchBrowserTestBase(true) {}
88};
89
90// URLRequestJob (and associated handler) which hangs.
91class HangingURLRequestJob : public net::URLRequestJob {
92 public:
93  HangingURLRequestJob(net::URLRequest* request,
94                       net::NetworkDelegate* network_delegate)
95      : net::URLRequestJob(request, network_delegate) {}
96
97  // net::URLRequestJob implementation
98  virtual void Start() OVERRIDE {}
99
100 private:
101  virtual ~HangingURLRequestJob() {}
102
103  DISALLOW_COPY_AND_ASSIGN(HangingURLRequestJob);
104};
105
106class HangingRequestInterceptor : public net::URLRequestInterceptor {
107 public:
108  explicit HangingRequestInterceptor(const base::Closure& callback)
109      : callback_(callback) {}
110
111  virtual ~HangingRequestInterceptor() {}
112
113  virtual net::URLRequestJob* MaybeInterceptRequest(
114      net::URLRequest* request,
115      net::NetworkDelegate* network_delegate) const OVERRIDE {
116    if (!callback_.is_null())
117      BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback_);
118    return new HangingURLRequestJob(request, network_delegate);
119  }
120
121 private:
122  base::Closure callback_;
123};
124
125void CreateHangingRequestInterceptorOnIO(const GURL& url,
126                                         base::Closure callback) {
127  CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
128  scoped_ptr<net::URLRequestInterceptor> never_respond_handler(
129      new HangingRequestInterceptor(callback));
130  net::URLRequestFilter::GetInstance()->AddUrlInterceptor(
131      url, never_respond_handler.Pass());
132}
133
134// Prefetch is disabled via field experiment.  Prefetch should be dropped.
135IN_PROC_BROWSER_TEST_F(PrefetchBrowserTestPredictionDisabled,
136                       ExperimentDisabled) {
137  CHECK(test_server()->Start());
138  EXPECT_TRUE(RunPrefetchExperiment(false, browser()));
139  // Should not prefetch even if preference is ALWAYS.
140  SetPreference(NetworkPredictionOptions::NETWORK_PREDICTION_ALWAYS);
141  EXPECT_TRUE(RunPrefetchExperiment(false, browser()));
142}
143
144// Prefetch should be allowed depending on preference and network type.
145IN_PROC_BROWSER_TEST_F(PrefetchBrowserTestPrediction, PreferenceWorks) {
146  CHECK(test_server()->Start());
147  // Set real NetworkChangeNotifier singleton aside.
148  scoped_ptr<NetworkChangeNotifier::DisableForTest> disable_for_test(
149      new NetworkChangeNotifier::DisableForTest);
150
151  // Preference defaults to WIFI_ONLY: prefetch when not on cellular.
152  {
153    scoped_ptr<NetworkChangeNotifier> mock(new MockNetworkChangeNotifierWIFI);
154    EXPECT_TRUE(RunPrefetchExperiment(true, browser()));
155  }
156  {
157    scoped_ptr<NetworkChangeNotifier> mock(new MockNetworkChangeNotifier4G);
158    EXPECT_TRUE(RunPrefetchExperiment(false, browser()));
159  }
160
161  // Set preference to ALWAYS: always prefetch.
162  SetPreference(NetworkPredictionOptions::NETWORK_PREDICTION_ALWAYS);
163  {
164    scoped_ptr<NetworkChangeNotifier> mock(new MockNetworkChangeNotifierWIFI);
165    EXPECT_TRUE(RunPrefetchExperiment(true, browser()));
166  }
167  {
168    scoped_ptr<NetworkChangeNotifier> mock(new MockNetworkChangeNotifier4G);
169    EXPECT_TRUE(RunPrefetchExperiment(true, browser()));
170  }
171
172  // Set preference to NEVER: never prefetch.
173  SetPreference(NetworkPredictionOptions::NETWORK_PREDICTION_NEVER);
174  {
175    scoped_ptr<NetworkChangeNotifier> mock(new MockNetworkChangeNotifierWIFI);
176    EXPECT_TRUE(RunPrefetchExperiment(false, browser()));
177  }
178  {
179    scoped_ptr<NetworkChangeNotifier> mock(new MockNetworkChangeNotifier4G);
180    EXPECT_TRUE(RunPrefetchExperiment(false, browser()));
181  }
182}
183
184// Bug 339909: When in incognito mode the browser crashed due to an
185// uninitialized preference member. Verify that it no longer does.
186IN_PROC_BROWSER_TEST_F(PrefetchBrowserTestPrediction, IncognitoTest) {
187  Profile* incognito_profile = browser()->profile()->GetOffTheRecordProfile();
188  Browser* incognito_browser = new Browser(
189      Browser::CreateParams(incognito_profile, browser()->host_desktop_type()));
190
191  // Navigate just to have a tab in this window, otherwise there is no
192  // WebContents for the incognito browser.
193  ui_test_utils::OpenURLOffTheRecord(browser()->profile(), GURL("about:blank"));
194
195  CHECK(test_server()->Start());
196  EXPECT_TRUE(RunPrefetchExperiment(true, incognito_browser));
197}
198
199// This test will verify the following:
200// - that prefetches from the browser are actually launched
201// - if a prefetch is in progress, but the originating renderer is destroyed,
202//   that the pending prefetch request is cleaned up cleanly and does not
203//   result in a crash.
204IN_PROC_BROWSER_TEST_F(PrefetchBrowserTestPrediction, PrefetchFromBrowser) {
205  const GURL kHangingUrl("http://hanging-url.com");
206  base::RunLoop loop_;
207  BrowserThread::PostTask(BrowserThread::IO,
208                          FROM_HERE,
209                          base::Bind(&CreateHangingRequestInterceptorOnIO,
210                                     kHangingUrl,
211                                     loop_.QuitClosure()));
212  ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
213  content::RenderFrameHost* rfh =
214      browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame();
215  rfh->Send(new PrefetchMsg_Prefetch(rfh->GetRoutingID(), kHangingUrl));
216  loop_.Run();
217}
218
219}  // namespace
220