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