// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/command_line.h" #include "base/prefs/pref_service.h" #include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/net/prediction_options.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" #include "chrome/common/prefetch_messages.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" #include "content/public/test/browser_test_utils.h" #include "net/base/network_change_notifier.h" #include "net/url_request/url_request_filter.h" #include "net/url_request/url_request_job.h" using chrome_browser_net::NetworkPredictionOptions; using content::BrowserThread; using net::NetworkChangeNotifier; namespace { const char kPrefetchPage[] = "files/prerender/simple_prefetch.html"; class MockNetworkChangeNotifierWIFI : public NetworkChangeNotifier { public: ConnectionType GetCurrentConnectionType() const override { return NetworkChangeNotifier::CONNECTION_WIFI; } }; class MockNetworkChangeNotifier4G : public NetworkChangeNotifier { public: ConnectionType GetCurrentConnectionType() const override { return NetworkChangeNotifier::CONNECTION_4G; } }; class PrefetchBrowserTestBase : public InProcessBrowserTest { public: explicit PrefetchBrowserTestBase(bool disabled_via_field_trial) : disabled_via_field_trial_(disabled_via_field_trial) {} void SetUpCommandLine(CommandLine* command_line) override { if (disabled_via_field_trial_) { command_line->AppendSwitchASCII(switches::kForceFieldTrials, "Prefetch/ExperimentDisabled/"); } } void SetPreference(NetworkPredictionOptions value) { browser()->profile()->GetPrefs()->SetInteger( prefs::kNetworkPredictionOptions, value); } bool RunPrefetchExperiment(bool expect_success, Browser* browser) { GURL url = test_server()->GetURL(kPrefetchPage); const base::string16 expected_title = expect_success ? base::ASCIIToUTF16("link onload") : base::ASCIIToUTF16("link onerror"); content::TitleWatcher title_watcher( browser->tab_strip_model()->GetActiveWebContents(), expected_title); ui_test_utils::NavigateToURL(browser, url); return expected_title == title_watcher.WaitAndGetTitle(); } private: bool disabled_via_field_trial_; }; class PrefetchBrowserTestPrediction : public PrefetchBrowserTestBase { public: PrefetchBrowserTestPrediction() : PrefetchBrowserTestBase(false) {} }; class PrefetchBrowserTestPredictionDisabled : public PrefetchBrowserTestBase { public: PrefetchBrowserTestPredictionDisabled() : PrefetchBrowserTestBase(true) {} }; // URLRequestJob (and associated handler) which hangs. class HangingURLRequestJob : public net::URLRequestJob { public: HangingURLRequestJob(net::URLRequest* request, net::NetworkDelegate* network_delegate) : net::URLRequestJob(request, network_delegate) {} // net::URLRequestJob implementation void Start() override {} private: ~HangingURLRequestJob() override {} DISALLOW_COPY_AND_ASSIGN(HangingURLRequestJob); }; class HangingRequestInterceptor : public net::URLRequestInterceptor { public: explicit HangingRequestInterceptor(const base::Closure& callback) : callback_(callback) {} ~HangingRequestInterceptor() override {} net::URLRequestJob* MaybeInterceptRequest( net::URLRequest* request, net::NetworkDelegate* network_delegate) const override { if (!callback_.is_null()) BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback_); return new HangingURLRequestJob(request, network_delegate); } private: base::Closure callback_; }; void CreateHangingRequestInterceptorOnIO(const GURL& url, base::Closure callback) { CHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); scoped_ptr never_respond_handler( new HangingRequestInterceptor(callback)); net::URLRequestFilter::GetInstance()->AddUrlInterceptor( url, never_respond_handler.Pass()); } // Prefetch is disabled via field experiment. Prefetch should be dropped. IN_PROC_BROWSER_TEST_F(PrefetchBrowserTestPredictionDisabled, ExperimentDisabled) { CHECK(test_server()->Start()); EXPECT_TRUE(RunPrefetchExperiment(false, browser())); // Should not prefetch even if preference is ALWAYS. SetPreference(NetworkPredictionOptions::NETWORK_PREDICTION_ALWAYS); EXPECT_TRUE(RunPrefetchExperiment(false, browser())); } // Prefetch should be allowed depending on preference and network type. IN_PROC_BROWSER_TEST_F(PrefetchBrowserTestPrediction, PreferenceWorks) { CHECK(test_server()->Start()); // Set real NetworkChangeNotifier singleton aside. scoped_ptr disable_for_test( new NetworkChangeNotifier::DisableForTest); // Preference defaults to WIFI_ONLY: prefetch when not on cellular. { scoped_ptr mock(new MockNetworkChangeNotifierWIFI); EXPECT_TRUE(RunPrefetchExperiment(true, browser())); } { scoped_ptr mock(new MockNetworkChangeNotifier4G); EXPECT_TRUE(RunPrefetchExperiment(false, browser())); } // Set preference to ALWAYS: always prefetch. SetPreference(NetworkPredictionOptions::NETWORK_PREDICTION_ALWAYS); { scoped_ptr mock(new MockNetworkChangeNotifierWIFI); EXPECT_TRUE(RunPrefetchExperiment(true, browser())); } { scoped_ptr mock(new MockNetworkChangeNotifier4G); EXPECT_TRUE(RunPrefetchExperiment(true, browser())); } // Set preference to NEVER: never prefetch. SetPreference(NetworkPredictionOptions::NETWORK_PREDICTION_NEVER); { scoped_ptr mock(new MockNetworkChangeNotifierWIFI); EXPECT_TRUE(RunPrefetchExperiment(false, browser())); } { scoped_ptr mock(new MockNetworkChangeNotifier4G); EXPECT_TRUE(RunPrefetchExperiment(false, browser())); } } // Bug 339909: When in incognito mode the browser crashed due to an // uninitialized preference member. Verify that it no longer does. IN_PROC_BROWSER_TEST_F(PrefetchBrowserTestPrediction, IncognitoTest) { Profile* incognito_profile = browser()->profile()->GetOffTheRecordProfile(); Browser* incognito_browser = new Browser( Browser::CreateParams(incognito_profile, browser()->host_desktop_type())); // Navigate just to have a tab in this window, otherwise there is no // WebContents for the incognito browser. ui_test_utils::OpenURLOffTheRecord(browser()->profile(), GURL("about:blank")); CHECK(test_server()->Start()); EXPECT_TRUE(RunPrefetchExperiment(true, incognito_browser)); } // This test will verify the following: // - that prefetches from the browser are actually launched // - if a prefetch is in progress, but the originating renderer is destroyed, // that the pending prefetch request is cleaned up cleanly and does not // result in a crash. IN_PROC_BROWSER_TEST_F(PrefetchBrowserTestPrediction, PrefetchFromBrowser) { const GURL kHangingUrl("http://hanging-url.com"); base::RunLoop loop_; BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(&CreateHangingRequestInterceptorOnIO, kHangingUrl, loop_.QuitClosure())); ui_test_utils::NavigateToURL(browser(), GURL("about:blank")); content::RenderFrameHost* rfh = browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame(); rfh->Send(new PrefetchMsg_Prefetch(rfh->GetRoutingID(), kHangingUrl)); loop_.Run(); } } // namespace