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 25 using chrome_browser_net::NetworkPredictionOptions; 26 using content::BrowserThread; 27 using net::NetworkChangeNotifier; 28 29 namespace { 30 31 const char kPrefetchPage[] = "files/prerender/simple_prefetch.html"; 32 33 class MockNetworkChangeNotifierWIFI : public NetworkChangeNotifier { 34 public: 35 virtual ConnectionType GetCurrentConnectionType() const OVERRIDE { 36 return NetworkChangeNotifier::CONNECTION_WIFI; 37 } 38 }; 39 40 class MockNetworkChangeNotifier4G : public NetworkChangeNotifier { 41 public: 42 virtual ConnectionType GetCurrentConnectionType() const OVERRIDE { 43 return NetworkChangeNotifier::CONNECTION_4G; 44 } 45 }; 46 47 class 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 80 class PrefetchBrowserTestPrediction : public PrefetchBrowserTestBase { 81 public: 82 PrefetchBrowserTestPrediction() : PrefetchBrowserTestBase(false) {} 83 }; 84 85 class PrefetchBrowserTestPredictionDisabled : public PrefetchBrowserTestBase { 86 public: 87 PrefetchBrowserTestPredictionDisabled() : PrefetchBrowserTestBase(true) {} 88 }; 89 90 // URLRequestJob (and associated handler) which hangs. 91 class 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 106 class 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 125 void 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. 135 IN_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. 145 IN_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. 186 IN_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. 204 IN_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