blob: 44be880c9769bc5ba312d5832eb13182f29cc9b7 [file] [log] [blame]
// Copyright (c) 2012 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/strings/string16.h"
#include "base/strings/string_split.h"
#include "base/task_scheduler/post_task.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/history/history_test_utils.h"
#include "chrome/browser/prerender/prerender_handle.h"
#include "chrome/browser/prerender/prerender_manager.h"
#include "chrome/browser/prerender/prerender_manager_factory.h"
#include "chrome/browser/prerender/prerender_test_utils.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/task_manager/task_manager_browsertest_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/browser_test_utils.h"
#include "net/base/escape.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/request_handler_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
using prerender::test_utils::CreateCountingInterceptorOnIO;
using prerender::test_utils::CreatePrefetchOnlyInterceptorOnIO;
using prerender::test_utils::DestructionWaiter;
using prerender::test_utils::RequestCounter;
using prerender::test_utils::TestPrerender;
namespace prerender {
// These URLs used for test resources must be relative with the exception of
// |kPrefetchLoaderPath|.
const char kPrefetchImagePage[] = "prerender/prefetch_image.html";
const char kPrefetchJpeg[] = "prerender/image.jpeg";
const char kPrefetchLoaderPath[] = "/prerender/prefetch_loader.html";
const char kPrefetchLoopPage[] = "prerender/prefetch_loop.html";
const char kPrefetchMetaCSP[] = "prerender/prefetch_meta_csp.html";
const char kPrefetchPage[] = "prerender/prefetch_page.html";
const char kPrefetchPage2[] = "prerender/prefetch_page2.html";
const char kPrefetchPng[] = "prerender/image.png";
const char kPrefetchResponseHeaderCSP[] =
"prerender/prefetch_response_csp.html";
const char kPrefetchScript[] = "prerender/prefetch.js";
const char kPrefetchScript2[] = "prerender/prefetch2.js";
const char kPrefetchSubresourceRedirectPage[] =
"prerender/prefetch_subresource_redirect.html";
class NoStatePrefetchBrowserTest
: public test_utils::PrerenderInProcessBrowserTest {
public:
NoStatePrefetchBrowserTest() {}
void SetUpOnMainThread() override {
test_utils::PrerenderInProcessBrowserTest::SetUpOnMainThread();
PrerenderManager::SetMode(
PrerenderManager::PRERENDER_MODE_NOSTATE_PREFETCH);
}
// Set up a request counter for |path|, which is also the location of the data
// served by the request.
void CountRequestFor(const std::string& path_str, RequestCounter* counter) {
url::StringPieceReplacements<base::FilePath::StringType> replacement;
base::FilePath file_path = base::FilePath::FromUTF8Unsafe(path_str);
replacement.SetPathStr(file_path.value());
const GURL url = src_server()->base_url().ReplaceComponents(replacement);
CountRequestForUrl(url, path_str, counter);
}
// As above, but specify the data path and URL separately.
void CountRequestForUrl(const GURL& url,
const std::string& path_str,
RequestCounter* counter) {
base::FilePath url_file = ui_test_utils::GetTestFilePath(
base::FilePath(), base::FilePath::FromUTF8Unsafe(path_str));
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&CreateCountingInterceptorOnIO, url, url_file,
counter->AsWeakPtr()));
}
protected:
std::unique_ptr<TestPrerender> PrefetchFromURL(
const GURL& target_url,
FinalStatus expected_final_status) {
GURL loader_url = ServeLoaderURL(
kPrefetchLoaderPath, "REPLACE_WITH_PREFETCH_URL", target_url, "");
std::vector<FinalStatus> expected_final_status_queue(1,
expected_final_status);
std::vector<std::unique_ptr<TestPrerender>> prerenders =
NavigateWithPrerenders(loader_url, expected_final_status_queue);
prerenders[0]->WaitForStop();
return std::move(prerenders[0]);
}
std::unique_ptr<TestPrerender> PrefetchFromFile(
const std::string& html_file,
FinalStatus expected_final_status) {
return PrefetchFromURL(src_server()->GetURL(MakeAbsolute(html_file)),
expected_final_status);
}
private:
DISALLOW_COPY_AND_ASSIGN(NoStatePrefetchBrowserTest);
};
// Checks that a page is correctly prefetched in the case of a
// <link rel=prerender> tag and the JavaScript on the page is not executed.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchSimple) {
RequestCounter main_counter;
CountRequestFor(kPrefetchPage, &main_counter);
RequestCounter script_counter;
CountRequestFor(kPrefetchScript, &script_counter);
RequestCounter script2_counter;
CountRequestFor(kPrefetchScript2, &script2_counter);
std::unique_ptr<TestPrerender> test_prerender =
PrefetchFromFile(kPrefetchPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
main_counter.WaitForCount(1);
script_counter.WaitForCount(1);
script2_counter.WaitForCount(0);
// Verify that the page load did not happen.
test_prerender->WaitForLoads(0);
}
// Check that the LOAD_PREFETCH flag is set.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchLoadFlag) {
RequestCounter main_counter;
RequestCounter script_counter;
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&CreatePrefetchOnlyInterceptorOnIO,
src_server()->GetURL(MakeAbsolute(kPrefetchPage)),
main_counter.AsWeakPtr()));
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&CreatePrefetchOnlyInterceptorOnIO,
src_server()->GetURL(MakeAbsolute(kPrefetchScript)),
script_counter.AsWeakPtr()));
std::unique_ptr<TestPrerender> test_prerender =
PrefetchFromFile(kPrefetchPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
main_counter.WaitForCount(1);
script_counter.WaitForCount(1);
// Verify that the page load did not happen.
test_prerender->WaitForLoads(0);
}
// Checks the prefetch of an img tag.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchImage) {
RequestCounter image_counter;
CountRequestFor(kPrefetchJpeg, &image_counter);
base::StringPairs replacement_text;
replacement_text.push_back(
std::make_pair("REPLACE_WITH_IMAGE_URL", MakeAbsolute(kPrefetchJpeg)));
std::string main_page_path;
net::test_server::GetFilePathWithReplacements(
kPrefetchImagePage, replacement_text, &main_page_path);
// Note CountRequestFor cannot be used on the main page as the test server
// must handling the image url replacement.
PrefetchFromFile(main_page_path, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
image_counter.WaitForCount(1);
}
// Checks that a cross-domain prefetching works correctly.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchCrossDomain) {
static const std::string secondary_domain = "www.foo.com";
host_resolver()->AddRule(secondary_domain, "127.0.0.1");
GURL cross_domain_url(base::StringPrintf(
"http://%s:%d/%s", secondary_domain.c_str(),
embedded_test_server()->host_port_pair().port(), kPrefetchPage));
RequestCounter cross_domain_counter;
CountRequestForUrl(cross_domain_url, kPrefetchPage, &cross_domain_counter);
PrefetchFromURL(cross_domain_url, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
cross_domain_counter.WaitForCount(1);
}
// Checks that response header CSP is respected.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, ResponseHeaderCSP) {
static const std::string secondary_domain = "foo.bar";
host_resolver()->AddRule(secondary_domain, "127.0.0.1");
RequestCounter main_page;
CountRequestFor(kPrefetchResponseHeaderCSP, &main_page);
RequestCounter first_script;
CountRequestFor(kPrefetchScript, &first_script);
RequestCounter second_script;
GURL second_script_url(std::string("http://foo.bar/") + kPrefetchScript2);
CountRequestForUrl(second_script_url, kPrefetchScript2, &second_script);
PrefetchFromFile(kPrefetchResponseHeaderCSP,
FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
// The second script is in the correct domain for CSP, but the first script is
// not.
main_page.WaitForCount(1);
second_script.WaitForCount(1);
first_script.WaitForCount(0);
}
// Checks that CSP in the meta tag cancels the prefetch.
// TODO(mattcary): probably this behavior should be consistent with
// response-header CSP. See crbug/656581.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, MetaTagCSP) {
static const std::string secondary_domain = "foo.bar";
host_resolver()->AddRule(secondary_domain, "127.0.0.1");
RequestCounter main_page;
CountRequestFor(kPrefetchMetaCSP, &main_page);
RequestCounter first_script;
CountRequestFor(kPrefetchScript, &first_script);
RequestCounter second_script;
GURL second_script_url(std::string("http://foo.bar/") + kPrefetchScript2);
CountRequestForUrl(second_script_url, kPrefetchScript2, &second_script);
PrefetchFromFile(kPrefetchMetaCSP, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
// TODO(mattcary): See test comment above. If the meta CSP tag were parsed,
// |second_script| would be loaded. Instead as the background scanner bails as
// soon as the meta CSP tag is seen, only |main_page| is fetched.
main_page.WaitForCount(1);
second_script.WaitForCount(0);
first_script.WaitForCount(0);
}
// Checks that the second prefetch request succeeds. This test waits for
// Prerender Stop before starting the second request.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchMultipleRequest) {
RequestCounter first_main_counter;
CountRequestFor(kPrefetchPage, &first_main_counter);
RequestCounter second_main_counter;
CountRequestFor(kPrefetchPage2, &second_main_counter);
RequestCounter first_script_counter;
CountRequestFor(kPrefetchScript, &first_script_counter);
RequestCounter second_script_counter;
CountRequestFor(kPrefetchScript2, &second_script_counter);
PrefetchFromFile(kPrefetchPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
PrefetchFromFile(kPrefetchPage2, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
first_main_counter.WaitForCount(1);
second_main_counter.WaitForCount(1);
first_script_counter.WaitForCount(1);
second_script_counter.WaitForCount(1);
}
// Checks that a second prefetch request, started before the first stops,
// succeeds.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchSimultaneous) {
RequestCounter second_main_counter;
CountRequestFor(kPrefetchPage2, &second_main_counter);
RequestCounter second_script_counter;
CountRequestFor(kPrefetchScript2, &second_script_counter);
GURL first_url = src_server()->GetURL(MakeAbsolute(kPrefetchPage));
base::FilePath first_path = ui_test_utils::GetTestFilePath(
base::FilePath(), base::FilePath().AppendASCII(kPrefetchPage));
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&test_utils::CreateHangingFirstRequestInterceptorOnIO,
first_url, first_path, base::Closure()));
// Start the first prefetch directly instead of via PrefetchFromFile for the
// first prefetch to avoid the wait on prerender stop.
GURL first_loader_url = ServeLoaderURL(
kPrefetchLoaderPath, "REPLACE_WITH_PREFETCH_URL", first_url, "");
std::vector<FinalStatus> first_expected_status_queue(1,
FINAL_STATUS_CANCELLED);
NavigateWithPrerenders(first_loader_url, first_expected_status_queue);
PrefetchFromFile(kPrefetchPage2, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
second_main_counter.WaitForCount(1);
second_script_counter.WaitForCount(1);
}
// Checks a prefetch to a nonexisting page.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchNonexisting) {
std::unique_ptr<TestPrerender> test_prerender = PrefetchFromFile(
"nonexisting-page.html", FINAL_STATUS_UNSUPPORTED_SCHEME);
}
// Checks that a 301 redirect is followed.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, Prefetch301Redirect) {
RequestCounter script_counter;
CountRequestFor(kPrefetchScript, &script_counter);
PrefetchFromFile(
"/server-redirect/?" +
net::EscapeQueryParamValue(MakeAbsolute(kPrefetchPage), false),
FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
script_counter.WaitForCount(1);
}
// Checks that the load flags are set correctly for all resources in a 301
// redirect chain.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, Prefetch301LoadFlags) {
std::string redirect_path =
"/server-redirect/?" +
net::EscapeQueryParamValue(MakeAbsolute(kPrefetchPage), false);
GURL redirect_url = src_server()->GetURL(redirect_path);
GURL page_url = src_server()->GetURL(MakeAbsolute(kPrefetchPage));
RequestCounter redirect_counter;
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&CreatePrefetchOnlyInterceptorOnIO, redirect_url,
redirect_counter.AsWeakPtr()));
RequestCounter page_counter;
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::Bind(&CreatePrefetchOnlyInterceptorOnIO, page_url,
page_counter.AsWeakPtr()));
PrefetchFromFile(redirect_path, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
redirect_counter.WaitForCount(1);
page_counter.WaitForCount(1);
}
// Checks that a subresource 301 redirect is followed.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, Prefetch301Subresource) {
RequestCounter script_counter;
CountRequestFor(kPrefetchScript, &script_counter);
PrefetchFromFile(kPrefetchSubresourceRedirectPage,
FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
script_counter.WaitForCount(1);
}
// Checks a client redirect is not followed.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchClientRedirect) {
RequestCounter script_counter;
CountRequestFor(kPrefetchScript, &script_counter);
// A complete load of kPrefetchPage2 is used as a sentinal. Otherwise the test
// ends before script_counter would reliably see the load of kPrefetchScript,
// were it to happen.
RequestCounter sentinel_counter;
CountRequestFor(kPrefetchScript2, &sentinel_counter);
PrefetchFromFile(
"/client-redirect/?" +
net::EscapeQueryParamValue(MakeAbsolute(kPrefetchPage), false),
FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
ui_test_utils::NavigateToURL(
current_browser(), src_server()->GetURL(MakeAbsolute(kPrefetchPage2)));
sentinel_counter.WaitForCount(1);
script_counter.WaitForCount(0);
}
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchHttps) {
UseHttpsSrcServer();
RequestCounter main_counter;
CountRequestFor(kPrefetchPage, &main_counter);
RequestCounter script_counter;
CountRequestFor(kPrefetchScript, &script_counter);
PrefetchFromFile(kPrefetchPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
main_counter.WaitForCount(1);
script_counter.WaitForCount(1);
}
// Checks that an SSL error prevents prefetch.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, SSLError) {
// Only send the loaded page, not the loader, through SSL.
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME);
https_server.ServeFilesFromSourceDirectory("chrome/test/data");
ASSERT_TRUE(https_server.Start());
std::unique_ptr<TestPrerender> prerender = PrefetchFromURL(
https_server.GetURL(MakeAbsolute(kPrefetchPage)), FINAL_STATUS_SSL_ERROR);
DestructionWaiter waiter(prerender->contents(), FINAL_STATUS_SSL_ERROR);
EXPECT_TRUE(waiter.WaitForDestroy());
}
// Checks that a subresource failing SSL does not prevent prefetch on the rest
// of the page.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, SSLSubresourceError) {
// First confirm that the image loads as expected.
// A separate HTTPS server is started for the subresource; src_server() is
// non-HTTPS.
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_MISMATCHED_NAME);
https_server.ServeFilesFromSourceDirectory("chrome/test/data");
ASSERT_TRUE(https_server.Start());
GURL https_url = https_server.GetURL("/prerender/image.jpeg");
base::StringPairs replacement_text;
replacement_text.push_back(
std::make_pair("REPLACE_WITH_IMAGE_URL", https_url.spec()));
std::string main_page_path;
net::test_server::GetFilePathWithReplacements(
kPrefetchImagePage, replacement_text, &main_page_path);
RequestCounter script_counter;
CountRequestFor(kPrefetchScript, &script_counter);
std::unique_ptr<TestPrerender> prerender =
PrefetchFromFile(main_page_path, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
// Checks that the presumed failure of the image load didn't affect the script
// fetch. This assumes waiting for the script load is enough to see any error
// from the image load.
script_counter.WaitForCount(1);
}
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, Loop) {
RequestCounter script_counter;
CountRequestFor(kPrefetchScript, &script_counter);
RequestCounter main_counter;
CountRequestFor(kPrefetchLoopPage, &main_counter);
std::unique_ptr<TestPrerender> test_prerender = PrefetchFromFile(
kPrefetchLoopPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
main_counter.WaitForCount(1);
script_counter.WaitForCount(1);
}
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, RendererCrash) {
// Navigate to about:blank to get the session storage namespace.
ui_test_utils::NavigateToURL(current_browser(), GURL(url::kAboutBlankURL));
content::SessionStorageNamespace* storage_namespace =
GetActiveWebContents()
->GetController()
.GetDefaultSessionStorageNamespace();
// Navigate to about:crash without an intermediate loader because chrome://
// URLs are ignored in renderers, and the test server has no support for them.
const gfx::Size kSize(640, 480);
std::unique_ptr<TestPrerender> test_prerender =
prerender_contents_factory()->ExpectPrerenderContents(
FINAL_STATUS_RENDERER_CRASHED);
std::unique_ptr<PrerenderHandle> prerender_handle(
GetPrerenderManager()->AddPrerenderFromExternalRequest(
GURL(content::kChromeUICrashURL), content::Referrer(),
storage_namespace, gfx::Rect(kSize)));
ASSERT_EQ(prerender_handle->contents(), test_prerender->contents());
test_prerender->WaitForStop();
}
// Checks that the prefetch of png correctly loads the png.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, Png) {
RequestCounter counter;
CountRequestFor(kPrefetchPng, &counter);
PrefetchFromFile(kPrefetchPng, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
counter.WaitForCount(1);
}
// Checks that the prefetch of png correctly loads the jpeg.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, Jpeg) {
RequestCounter counter;
CountRequestFor(kPrefetchJpeg, &counter);
PrefetchFromFile(kPrefetchJpeg, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
counter.WaitForCount(1);
}
// Checks that nothing is prefetched from malware sites.
// TODO(mattcary): disabled as prefetch process teardown is racey with prerender
// contents destruction, can fix when prefetch prerenderers are destroyed
// deterministically.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest,
DISABLED_PrerenderSafeBrowsingTopLevel) {
GURL url = src_server()->GetURL(MakeAbsolute(kPrefetchPage));
GetFakeSafeBrowsingDatabaseManager()->SetThreatTypeForUrl(
url, safe_browsing::SB_THREAT_TYPE_URL_MALWARE);
// Prefetch resources are blocked, but the prerender is not killed in any
// special way.
// TODO(mattcary): since the prerender will count itself as loaded even if the
// fetch of the main resource fails, the test doesn't actually confirm what we
// want it to confirm. This may be fixed by planned changes to the prerender
// lifecycle.
std::unique_ptr<TestPrerender> prerender =
PrefetchFromFile(kPrefetchPage, FINAL_STATUS_SAFE_BROWSING);
}
// Checks that prefetching a page does not add it to browsing history.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, HistoryUntouchedByPrefetch) {
// Initialize.
Profile* profile = current_browser()->profile();
ASSERT_TRUE(profile);
ui_test_utils::WaitForHistoryToLoad(HistoryServiceFactory::GetForProfile(
profile, ServiceAccessType::EXPLICIT_ACCESS));
// Prefetch a page.
GURL prefetched_url = src_server()->GetURL(MakeAbsolute(kPrefetchPage));
PrefetchFromFile(kPrefetchPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForHistoryBackendToRun(profile);
// Navigate to another page.
GURL navigated_url = src_server()->GetURL(MakeAbsolute(kPrefetchPage2));
ui_test_utils::NavigateToURL(current_browser(), navigated_url);
WaitForHistoryBackendToRun(profile);
// Check that the URL that was explicitly navigated to is already in history.
ui_test_utils::HistoryEnumerator enumerator(profile);
std::vector<GURL>& urls = enumerator.urls();
EXPECT_TRUE(std::find(urls.begin(), urls.end(), navigated_url) != urls.end());
// Check that the URL that was prefetched is not in history.
EXPECT_TRUE(std::find(urls.begin(), urls.end(), prefetched_url) ==
urls.end());
// The loader URL is the remaining entry.
EXPECT_EQ(2U, urls.size());
}
} // namespace prerender