blob: c6b6b96bc9af13c6de097a551f88a6c79567da89 [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/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/memory/ref_counted.h"
#include "base/metrics/statistics_recorder.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind_test_util.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/threading/platform_thread.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.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/browser_list.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/embedder_support/switches.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/appcache_service.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/result_codes.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/browsing_data_remover_test_util.h"
#include "content/public/test/no_renderer_crashes_assertion.h"
#include "content/public/test/url_loader_interceptor.h"
#include "content/public/test/url_loader_monitor.h"
#include "net/base/escape.h"
#include "net/base/features.h"
#include "net/base/load_flags.h"
#include "net/base/request_priority.h"
#include "net/cookies/cookie_store.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/request_handler_util.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/mojom/cookie_manager.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/mojom/appcache/appcache_info.mojom.h"
using prerender::test_utils::DestructionWaiter;
using prerender::test_utils::TestPrerender;
namespace {
const char kExpectedPurposeHeaderOnPrefetch[] = "Purpose";
std::string CreateServerRedirect(const std::string& dest_url) {
const char* const kServerRedirectBase = "/server-redirect?";
return kServerRedirectBase + net::EscapeQueryParamValue(dest_url, false);
}
// This is the public key of tools/origin_trials/eftest.key, used to validate
// origin trial tokens generated by tools/origin_trials/generate_token.py.
static constexpr char kOriginTrialPublicKeyForTesting[] =
"dRCs+TocuKkocNKa0AtZ4awrt9XKH2SQCI6o4FY6BNA=";
} // namespace
namespace prerender {
const char k302RedirectPage[] = "/prerender/302_redirect.html";
const char kPrefetchAppcache[] = "/prerender/prefetch_appcache.html";
const char kPrefetchAppcacheManifest[] = "/prerender/appcache.manifest";
const char kPrefetchCookiePage[] = "/prerender/cookie.html";
const char kPrefetchFromSubframe[] = "/prerender/prefetch_from_subframe.html";
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 kPrefetchNostorePage[] = "/prerender/prefetch_nostore_page.html";
const char kPrefetchPage[] = "/prerender/prefetch_page.html";
const char kPrefetchPageWithFragment[] =
"/prerender/prefetch_page.html#fragment";
const char kPrefetchPage2[] = "/prerender/prefetch_page2.html";
const char kPrefetchPageBigger[] = "/prerender/prefetch_page_bigger.html";
const char kPrefetchPageMultipleResourceTypes[] =
"/prerender/prefetch_page_multiple_resource_types.html";
const char kPrefetchPng[] = "/prerender/image.png";
const char kPrefetchPng2[] = "/prerender/image2.png";
const char kPrefetchPngRedirect[] = "/prerender/image-redirect.png";
const char kPrefetchRecursePage[] = "/prerender/prefetch_recurse.html";
const char kPrefetchResponseHeaderCSP[] =
"/prerender/prefetch_response_csp.html";
const char kPrefetchScript[] = "/prerender/prefetch.js";
const char kPrefetchScript2[] = "/prerender/prefetch2.js";
const char kPrefetchCss[] = "/prerender/style.css";
const char kPrefetchFont[] = "/prerender/font.woff";
const char kPrefetchDownloadFile[] = "/download-test1.lib";
const char kPrefetchSubresourceRedirectPage[] =
"/prerender/prefetch_subresource_redirect.html";
const char kServiceWorkerLoader[] = "/prerender/service_worker.html";
const char kHungPrerenderPage[] = "/prerender/hung_prerender_page.html";
// A navigation observer to wait on either a new load or a swap of a
// WebContents. On swap, if the new WebContents is still loading, wait for that
// load to complete as well. Note that the load must begin after the observer is
// attached.
class NavigationOrSwapObserver : public content::WebContentsObserver,
public TabStripModelObserver {
public:
// Waits for either a new load or a swap of |tab_strip_model|'s active
// WebContents.
NavigationOrSwapObserver(TabStripModel* tab_strip_model,
content::WebContents* web_contents)
: content::WebContentsObserver(web_contents),
tab_strip_model_(tab_strip_model),
did_start_loading_(false),
number_of_loads_(1) {
EXPECT_NE(TabStripModel::kNoTab,
tab_strip_model->GetIndexOfWebContents(web_contents));
tab_strip_model_->AddObserver(this);
}
// Waits for either |number_of_loads| loads or a swap of |tab_strip_model|'s
// active WebContents.
NavigationOrSwapObserver(TabStripModel* tab_strip_model,
content::WebContents* web_contents,
int number_of_loads)
: content::WebContentsObserver(web_contents),
tab_strip_model_(tab_strip_model),
did_start_loading_(false),
number_of_loads_(number_of_loads) {
EXPECT_NE(TabStripModel::kNoTab,
tab_strip_model->GetIndexOfWebContents(web_contents));
tab_strip_model_->AddObserver(this);
}
~NavigationOrSwapObserver() override {
tab_strip_model_->RemoveObserver(this);
}
void set_did_start_loading() { did_start_loading_ = true; }
void Wait() { loop_.Run(); }
// content::WebContentsObserver implementation:
void DidStartLoading() override { did_start_loading_ = true; }
void DidStopLoading() override {
if (!did_start_loading_)
return;
number_of_loads_--;
if (number_of_loads_ == 0)
loop_.Quit();
}
// TabStripModelObserver implementation:
void OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) override {
if (change.type() != TabStripModelChange::kReplaced)
return;
auto* replace = change.GetReplace();
if (replace->old_contents != web_contents())
return;
// Switch to observing the new WebContents.
Observe(replace->new_contents);
if (replace->new_contents->IsLoading()) {
// If the new WebContents is still loading, wait for it to complete.
// Only one load post-swap is supported.
did_start_loading_ = true;
number_of_loads_ = 1;
} else {
loop_.Quit();
}
}
private:
TabStripModel* tab_strip_model_;
bool did_start_loading_;
int number_of_loads_;
base::RunLoop loop_;
};
// Waits for a new tab to open and a navigation or swap in it.
class NewTabNavigationOrSwapObserver : public TabStripModelObserver,
public BrowserListObserver {
public:
NewTabNavigationOrSwapObserver() {
BrowserList::AddObserver(this);
for (const Browser* browser : *BrowserList::GetInstance())
browser->tab_strip_model()->AddObserver(this);
}
~NewTabNavigationOrSwapObserver() override {
BrowserList::RemoveObserver(this);
}
void Wait() {
new_tab_run_loop_.Run();
swap_observer_->Wait();
}
// TabStripModelObserver:
void OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) override {
if (change.type() != TabStripModelChange::kInserted || swap_observer_)
return;
content::WebContents* new_tab = change.GetInsert()->contents[0].contents;
swap_observer_ =
std::make_unique<NavigationOrSwapObserver>(tab_strip_model, new_tab);
swap_observer_->set_did_start_loading();
new_tab_run_loop_.Quit();
}
// BrowserListObserver:
void OnBrowserAdded(Browser* browser) override {
browser->tab_strip_model()->AddObserver(this);
}
private:
base::RunLoop new_tab_run_loop_;
std::unique_ptr<NavigationOrSwapObserver> swap_observer_;
DISALLOW_COPY_AND_ASSIGN(NewTabNavigationOrSwapObserver);
};
class NoStatePrefetchBrowserTest
: public test_utils::PrerenderInProcessBrowserTest {
public:
NoStatePrefetchBrowserTest() {}
void SetUpDefaultCommandLine(base::CommandLine* command_line) override {
test_utils::PrerenderInProcessBrowserTest::SetUpDefaultCommandLine(
command_line);
command_line->AppendSwitchASCII(embedder_support::kOriginTrialPublicKey,
kOriginTrialPublicKeyForTesting);
}
void SetUpOnMainThread() override {
test_utils::PrerenderInProcessBrowserTest::SetUpOnMainThread();
PrerenderManager::SetMode(
PrerenderManager::PRERENDER_MODE_NOSTATE_PREFETCH);
host_resolver()->AddRule("*", "127.0.0.1");
}
void OverridePrerenderManagerTimeTicks() {
// The default zero time causes the prerender manager to do strange things.
clock_.Advance(base::TimeDelta::FromSeconds(1));
GetPrerenderManager()->SetTickClockForTesting(&clock_);
}
// Block until an AppCache exists for |manifest_url|.
void WaitForAppcache(const GURL& manifest_url) {
bool found_manifest = false;
content::AppCacheService* appcache_service =
content::BrowserContext::GetDefaultStoragePartition(
current_browser()->profile())
->GetAppCacheService();
do {
base::RunLoop wait_loop;
WaitForAppcache(manifest_url, appcache_service, base::DoNothing(),
&found_manifest);
// There seems to be some flakiness in the appcache getting back to us, so
// use a timeout task to try the appcache query again.
content::GetUIThreadTaskRunner({})->PostDelayedTask(
FROM_HERE, wait_loop.QuitClosure(),
base::TimeDelta::FromMilliseconds(2000));
wait_loop.Run();
} while (!found_manifest);
}
protected:
// Loads kPrefetchLoaderPath and specifies |target_url| as a query param. The
// |loader_url| looks something like:
// http://127.0.0.1:port_number/prerender/prefetch_loader.html?replace_text=\
// UkVQTEFDRV9XSVRIX1BSRUZFVENIX1VSTA==:aHR0cDovL3d3dy52dlci5odG1s.
// When the embedded test server receives the request, it uses the specified
// query params to replace the "REPLACE_WITH_PREFETCH_URL" string in the HTML
// response with |target_url|. See method UpdateReplacedText() from embedded
// test server.
std::unique_ptr<TestPrerender> PrefetchFromURL(
const GURL& target_url,
FinalStatus expected_final_status,
int expected_number_of_loads = 0) {
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]->WaitForLoads(0);
// Ensure that the referring page receives the right start and load events.
WaitForPrerenderStartEventForLinkNumber(0);
if (check_load_events_) {
WaitForPrerenderEventCount(0, "webkitprerenderload",
expected_number_of_loads);
}
if (ShouldAbortPrerenderBeforeSwap(expected_final_status_queue.front())) {
// The prerender will abort on its own. Assert it does so correctly.
prerenders[0]->WaitForStop();
EXPECT_FALSE(prerenders[0]->contents());
WaitForPrerenderStopEventForLinkNumber(0);
} else {
// Otherwise, check that it prerendered correctly.
test_utils::TestPrerenderContents* prerender_contents =
prerenders[0]->contents();
if (prerender_contents) {
EXPECT_EQ(FINAL_STATUS_UNKNOWN, prerender_contents->final_status());
EXPECT_FALSE(DidReceivePrerenderStopEventForLinkNumber(0));
}
}
// Test for proper event ordering.
EXPECT_FALSE(HadPrerenderEventErrors());
return std::move(prerenders[0]);
}
// Returns true if the prerender is expected to abort on its own, before
// attempting to swap it.
bool ShouldAbortPrerenderBeforeSwap(FinalStatus status) {
switch (status) {
case FINAL_STATUS_USED:
case FINAL_STATUS_APP_TERMINATING:
case FINAL_STATUS_PROFILE_DESTROYED:
case FINAL_STATUS_CACHE_OR_HISTORY_CLEARED:
// We'll crash the renderer after it's loaded.
case FINAL_STATUS_RENDERER_CRASHED:
case FINAL_STATUS_CANCELLED:
return false;
default:
return true;
}
}
void DisableLoadEventCheck() { check_load_events_ = false; }
std::unique_ptr<TestPrerender> PrefetchFromFile(
const std::string& html_file,
FinalStatus expected_final_status,
int expected_number_of_loads = 0) {
return PrefetchFromURL(src_server()->GetURL(MakeAbsolute(html_file)),
expected_final_status, expected_number_of_loads);
}
// Returns length of |prerender_manager_|'s history, or SIZE_MAX on failure.
size_t GetHistoryLength() const {
std::unique_ptr<base::DictionaryValue> prerender_dict =
GetPrerenderManager()->CopyAsValue();
if (!prerender_dict)
return std::numeric_limits<size_t>::max();
base::ListValue* history_list;
if (!prerender_dict->GetList("history", &history_list))
return std::numeric_limits<size_t>::max();
return history_list->GetSize();
}
// Clears the specified data using BrowsingDataRemover.
void ClearBrowsingData(Browser* browser, uint64_t remove_mask) {
content::BrowsingDataRemover* remover =
content::BrowserContext::GetBrowsingDataRemover(browser->profile());
content::BrowsingDataRemoverCompletionObserver observer(remover);
remover->RemoveAndReply(
base::Time(), base::Time::Max(), remove_mask,
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB, &observer);
observer.BlockUntilCompletion();
// BrowsingDataRemover deletes itself.
}
// Synchronization note: The IPCs used to communicate DOM events back to the
// referring web page (see blink::mojom::PrerenderHandleClient) may race w/
// the IPCs used here to inject script. The WaitFor* variants should be used
// when an event was expected to happen or to happen soon.
int GetPrerenderEventCount(int index, const std::string& type) const {
int event_count;
std::string expression = base::StringPrintf(
"window.domAutomationController.send("
" GetPrerenderEventCount(%d, '%s'))",
index, type.c_str());
CHECK(content::ExecuteScriptAndExtractInt(GetActiveWebContents(),
expression, &event_count));
return event_count;
}
bool DidReceivePrerenderStartEventForLinkNumber(int index) const {
return GetPrerenderEventCount(index, "webkitprerenderstart") > 0;
}
int GetPrerenderLoadEventCountForLinkNumber(int index) const {
return GetPrerenderEventCount(index, "webkitprerenderload");
}
bool DidReceivePrerenderStopEventForLinkNumber(int index) const {
return GetPrerenderEventCount(index, "webkitprerenderstop") > 0;
}
void WaitForPrerenderEventCount(int index,
const std::string& type,
int count) const {
int dummy;
std::string expression = base::StringPrintf(
"WaitForPrerenderEventCount(%d, '%s', %d,"
" window.domAutomationController.send.bind("
" window.domAutomationController, 0))",
index, type.c_str(), count);
CHECK(content::ExecuteScriptAndExtractInt(GetActiveWebContents(),
expression, &dummy));
CHECK_EQ(0, dummy);
}
void WaitForPrerenderStartEventForLinkNumber(int index) const {
WaitForPrerenderEventCount(index, "webkitprerenderstart", 1);
}
void WaitForPrerenderStopEventForLinkNumber(int index) const {
WaitForPrerenderEventCount(index, "webkitprerenderstart", 1);
}
bool HadPrerenderEventErrors() const {
bool had_prerender_event_errors;
CHECK(content::ExecuteScriptAndExtractBool(
GetActiveWebContents(),
"window.domAutomationController.send(Boolean("
" hadPrerenderEventErrors))",
&had_prerender_event_errors));
return had_prerender_event_errors;
}
// Opens the prerendered page using javascript functions in the loader
// page. |javascript_function_name| should be a 0 argument function which is
// invoked. |new_web_contents| is true if the navigation is expected to
// happen in a new WebContents via OpenURL.
void OpenURLWithJSImpl(const std::string& javascript_function_name,
const GURL& url,
const GURL& ping_url,
bool new_web_contents) const {
content::WebContents* web_contents = GetActiveWebContents();
content::RenderFrameHost* render_frame_host = web_contents->GetMainFrame();
// Extra arguments in JS are ignored.
std::string javascript =
base::StringPrintf("%s('%s', '%s')", javascript_function_name.c_str(),
url.spec().c_str(), ping_url.spec().c_str());
if (new_web_contents) {
NewTabNavigationOrSwapObserver observer;
render_frame_host->ExecuteJavaScriptWithUserGestureForTests(
base::ASCIIToUTF16(javascript));
observer.Wait();
} else {
NavigationOrSwapObserver observer(current_browser()->tab_strip_model(),
web_contents);
render_frame_host->ExecuteJavaScriptForTests(
base::ASCIIToUTF16(javascript), base::NullCallback());
observer.Wait();
}
}
void OpenDestURLViaClickNewWindow(GURL& dest_url) const {
OpenURLWithJSImpl("ShiftClick", dest_url, GURL(), true);
}
void OpenDestURLViaClickNewForegroundTab(GURL& dest_url) const {
#if defined(OS_MACOSX)
OpenURLWithJSImpl("MetaShiftClick", dest_url, GURL(), true);
#else
OpenURLWithJSImpl("CtrlShiftClick", dest_url, GURL(), true);
#endif
}
bool check_load_events_ = true;
base::SimpleTestTickClock clock_;
private:
// Schedule a task to retrieve AppCacheInfo from |appcache_service|. This sets
// |found_manifest| if an appcache exists for |manifest_url|. |callback| will
// be called on the UI thread after the info is retrieved, whether or not the
// manifest exists.
static void WaitForAppcache(const GURL& manifest_url,
content::AppCacheService* appcache_service,
base::Closure callback,
bool* found_manifest) {
scoped_refptr<content::AppCacheInfoCollection> info_collection =
new content::AppCacheInfoCollection();
appcache_service->GetAllAppCacheInfo(
info_collection.get(),
base::BindOnce(ProcessAppCacheInfo, manifest_url, callback,
found_manifest, info_collection));
}
// Look through |info_collection| for an entry matching |target_manifest|,
// setting |found_manifest| appropriately. Then |callback| will be invoked on
// the UI thread.
static void ProcessAppCacheInfo(
const GURL& target_manifest,
base::Closure callback,
bool* found_manifest,
scoped_refptr<content::AppCacheInfoCollection> info_collection,
int status) {
if (status == net::OK) {
for (const auto& origin_pair : info_collection->infos_by_origin) {
for (const auto& info : origin_pair.second) {
if (info.manifest_url == target_manifest) {
*found_manifest = true;
break;
}
}
}
}
content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, callback);
}
base::test::ScopedFeatureList feature_list_;
DISALLOW_COPY_AND_ASSIGN(NoStatePrefetchBrowserTest);
};
class NoStatePrefetchBrowserTestHttpCache
: public NoStatePrefetchBrowserTest,
public testing::WithParamInterface<std::tuple<bool, bool>> {
protected:
void SetUp() override {
bool split_cache_by_network_isolation_key;
bool append_frame_origin_to_network_isolation_key;
std::tie(split_cache_by_network_isolation_key,
append_frame_origin_to_network_isolation_key) = GetParam();
std::vector<base::Feature> disabled_and_enabled_features[2];
disabled_and_enabled_features[split_cache_by_network_isolation_key]
.push_back(net::features::kSplitCacheByNetworkIsolationKey);
disabled_and_enabled_features[append_frame_origin_to_network_isolation_key]
.push_back(net::features::kAppendFrameOriginToNetworkIsolationKey);
feature_list_.InitWithFeatures(disabled_and_enabled_features[true],
disabled_and_enabled_features[false]);
NoStatePrefetchBrowserTest::SetUp();
}
private:
base::test::ScopedFeatureList feature_list_;
};
using NoStatePrefetchBrowserTestHttpCache_DefaultAndAppendFrameOrigin =
NoStatePrefetchBrowserTestHttpCache;
// Test that the network isolation key is correctly populated during a prefetch,
// with feature kAppendFrameOriginToNetworkIsolationKey disabled and enabled
// respectively.
IN_PROC_BROWSER_TEST_P(
NoStatePrefetchBrowserTestHttpCache_DefaultAndAppendFrameOrigin,
PrefetchTwoCrossOriginFrames) {
bool append_frame_origin_to_network_isolation_key;
std::tie(std::ignore, append_frame_origin_to_network_isolation_key) =
GetParam();
GURL image_src =
embedded_test_server()->GetURL("/prerender/cacheable_image.png");
base::StringPairs replacement_text_img_src;
replacement_text_img_src.push_back(
std::make_pair("IMAGE_SRC", image_src.spec()));
std::string iframe_path = net::test_server::GetFilePathWithReplacements(
"/prerender/one_image.html", replacement_text_img_src);
GURL iframe_src_1 = embedded_test_server()->GetURL("www.a.com", iframe_path);
GURL iframe_src_2 = embedded_test_server()->GetURL("www.b.com", iframe_path);
base::StringPairs replacement_text_iframe_src;
replacement_text_iframe_src.push_back(
std::make_pair("IFRAME_1_SRC", iframe_src_1.spec()));
replacement_text_iframe_src.push_back(
std::make_pair("IFRAME_2_SRC", iframe_src_2.spec()));
std::string prerender_path = net::test_server::GetFilePathWithReplacements(
"/prerender/two_iframes.html", replacement_text_iframe_src);
ui_test_utils::NavigateToURL(current_browser(),
src_server()->GetURL(prerender_path));
WaitForRequestCount(image_src,
append_frame_origin_to_network_isolation_key ? 2 : 1);
}
INSTANTIATE_TEST_SUITE_P(
All,
NoStatePrefetchBrowserTestHttpCache_DefaultAndAppendFrameOrigin,
::testing::Combine(::testing::Values(true), ::testing::Bool()));
// 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) {
std::unique_ptr<TestPrerender> test_prerender =
PrefetchFromFile(kPrefetchPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
// Verify that the page load did not happen.
test_prerender->WaitForLoads(0);
WaitForRequestCount(src_server()->GetURL(kPrefetchPage), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 0);
}
// Checks that prefetching is not stopped forever by aggressive background load
// limits.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchBigger) {
std::unique_ptr<TestPrerender> test_prerender = PrefetchFromFile(
kPrefetchPageBigger, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForRequestCount(src_server()->GetURL(kPrefetchPageBigger), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchJpeg), 1);
// The |kPrefetchPng| is requested twice because the |kPrefetchPngRedirect|
// redirects to it.
WaitForRequestCount(src_server()->GetURL(kPrefetchPng), 2);
WaitForRequestCount(src_server()->GetURL(kPrefetchPng2), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchPngRedirect), 1);
}
using NoStatePrefetchBrowserTestHttpCache_DefaultAndDoubleKeyedHttpCache =
NoStatePrefetchBrowserTestHttpCache;
// Checks that a page load following a prefetch reuses preload-scanned resources
// and link rel 'prerender' main resource from cache without failing over to
// network.
IN_PROC_BROWSER_TEST_P(
NoStatePrefetchBrowserTestHttpCache_DefaultAndDoubleKeyedHttpCache,
LoadAfterPrefetch) {
{
std::unique_ptr<TestPrerender> test_prerender = PrefetchFromFile(
kPrefetchPageBigger, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForRequestCount(src_server()->GetURL(kPrefetchPageBigger), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchJpeg), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchPng2), 1);
}
ui_test_utils::NavigateToURL(current_browser(),
src_server()->GetURL(kPrefetchPageBigger));
// Check that the request counts did not increase.
WaitForRequestCount(src_server()->GetURL(kPrefetchPageBigger), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchJpeg), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchPng2), 1);
}
// Checks that a page load following a cross origin prefetch reuses
// preload-scanned resources and link rel 'prerender' main resource
// from cache without failing over to network.
IN_PROC_BROWSER_TEST_P(
NoStatePrefetchBrowserTestHttpCache_DefaultAndDoubleKeyedHttpCache,
LoadAfterPrefetchCrossOrigin) {
static const std::string kSecondaryDomain = "www.foo.com";
GURL cross_domain_url =
embedded_test_server()->GetURL(kSecondaryDomain, kPrefetchPageBigger);
PrefetchFromURL(cross_domain_url, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForRequestCount(src_server()->GetURL(kPrefetchPageBigger), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchJpeg), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchPng2), 1);
ui_test_utils::NavigateToURL(current_browser(), cross_domain_url);
// Check that the request counts did not increase.
WaitForRequestCount(src_server()->GetURL(kPrefetchPageBigger), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchJpeg), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchPng2), 1);
}
INSTANTIATE_TEST_SUITE_P(
All,
NoStatePrefetchBrowserTestHttpCache_DefaultAndDoubleKeyedHttpCache,
::testing::Combine(::testing::Bool(), ::testing::Values(false)));
// Checks that the expected resource types are fetched via NoState Prefetch.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchAllResourceTypes) {
std::unique_ptr<TestPrerender> test_prerender =
PrefetchFromFile(kPrefetchPageMultipleResourceTypes,
FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
// Verify that the page load did not happen.
test_prerender->WaitForLoads(0);
WaitForRequestCount(src_server()->GetURL(kPrefetchPageMultipleResourceTypes),
1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchPng), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchCss), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchFont), 1);
}
// Test and Test Class for lightweight prefetch under the HTML configuration.
class HTMLOnlyNoStatePrefetchBrowserTest : public NoStatePrefetchBrowserTest {
public:
void SetUp() override {
std::map<std::string, std::string> parameters;
parameters["html_only"] = "true";
feature_list_.InitWithFeaturesAndParameters(
{{blink::features::kLightweightNoStatePrefetch, parameters}}, {});
NoStatePrefetchBrowserTest::SetUp();
}
private:
base::test::ScopedFeatureList feature_list_;
};
// Checks that the expected resource types are fetched via NoState Prefetch.
IN_PROC_BROWSER_TEST_F(HTMLOnlyNoStatePrefetchBrowserTest, PrefetchHTMLOnly) {
std::unique_ptr<TestPrerender> test_prerender =
PrefetchFromFile(kPrefetchPageMultipleResourceTypes,
FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
// Verify that the page load did not happen.
test_prerender->WaitForLoads(0);
WaitForRequestCount(src_server()->GetURL(kPrefetchPageMultipleResourceTypes),
1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 0);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 0);
WaitForRequestCount(src_server()->GetURL(kPrefetchPng), 0);
WaitForRequestCount(src_server()->GetURL(kPrefetchCss), 0);
WaitForRequestCount(src_server()->GetURL(kPrefetchFont), 0);
}
// Test and Test Class for lightweight prefetch under the HTML+CSS
// configuration.
class HTMLCSSNoStatePrefetchBrowserTest : public NoStatePrefetchBrowserTest {
public:
void SetUp() override {
std::map<std::string, std::string> parameters;
parameters["skip_script"] = "true";
parameters["skip_other"] = "true";
feature_list_.InitWithFeaturesAndParameters(
{{blink::features::kLightweightNoStatePrefetch, parameters}}, {});
NoStatePrefetchBrowserTest::SetUp();
}
private:
base::test::ScopedFeatureList feature_list_;
};
// Checks that the expected resource types are fetched via NoState Prefetch.
IN_PROC_BROWSER_TEST_F(HTMLCSSNoStatePrefetchBrowserTest, PrefetchHTMLCSS) {
std::unique_ptr<TestPrerender> test_prerender =
PrefetchFromFile(kPrefetchPageMultipleResourceTypes,
FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
// Verify that the page load did not happen.
test_prerender->WaitForLoads(0);
WaitForRequestCount(src_server()->GetURL(kPrefetchPageMultipleResourceTypes),
1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 0);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 0);
WaitForRequestCount(src_server()->GetURL(kPrefetchPng), 0);
WaitForRequestCount(src_server()->GetURL(kPrefetchCss), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchFont), 0);
}
// Test and Test Class for lightweight prefetch under the HTML+CSS+SyncScript
// configuration.
class HTMLCSSSyncScriptNoStatePrefetchBrowserTest
: public NoStatePrefetchBrowserTest {
public:
void SetUp() override {
std::map<std::string, std::string> parameters;
parameters["skip_other"] = "true";
parameters["skip_async_script"] = "true";
feature_list_.InitWithFeaturesAndParameters(
{{blink::features::kLightweightNoStatePrefetch, parameters}}, {});
NoStatePrefetchBrowserTest::SetUp();
}
private:
base::test::ScopedFeatureList feature_list_;
};
// Checks that the expected resource types are fetched via NoState Prefetch.
IN_PROC_BROWSER_TEST_F(HTMLCSSSyncScriptNoStatePrefetchBrowserTest,
PrefetchHTMLCSSSyncScript) {
std::unique_ptr<TestPrerender> test_prerender =
PrefetchFromFile(kPrefetchPageMultipleResourceTypes,
FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
// Verify that the page load did not happen.
test_prerender->WaitForLoads(0);
WaitForRequestCount(src_server()->GetURL(kPrefetchPageMultipleResourceTypes),
1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 0);
WaitForRequestCount(src_server()->GetURL(kPrefetchPng), 0);
WaitForRequestCount(src_server()->GetURL(kPrefetchCss), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchFont), 0);
}
// Test and Test Class for lightweight prefetch under the
// HTML+CSS+SyncScript+Font configuration.
class HTMLCSSSyncScriptFontNoStatePrefetchBrowserTest
: public NoStatePrefetchBrowserTest {
public:
void SetUp() override {
std::map<std::string, std::string> parameters;
parameters["skip_other"] = "true";
parameters["skip_async_script"] = "true";
feature_list_.InitWithFeaturesAndParameters(
{{blink::features::kLightweightNoStatePrefetch, parameters}}, {});
NoStatePrefetchBrowserTest::SetUp();
}
private:
base::test::ScopedFeatureList feature_list_;
};
// Checks that the expected resource types are fetched via NoState Prefetch.
IN_PROC_BROWSER_TEST_F(HTMLCSSSyncScriptFontNoStatePrefetchBrowserTest,
PrefetchHTMLCSSSyncScript) {
std::unique_ptr<TestPrerender> test_prerender =
PrefetchFromFile(kPrefetchPageMultipleResourceTypes,
FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
// Verify that the page load did not happen.
test_prerender->WaitForLoads(0);
WaitForRequestCount(src_server()->GetURL(kPrefetchPageMultipleResourceTypes),
1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 0);
WaitForRequestCount(src_server()->GetURL(kPrefetchPng), 0);
WaitForRequestCount(src_server()->GetURL(kPrefetchCss), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchFont), 0);
}
// Test and Test Class for lightweight prefetch under the HTML+CSS+Script
// configuration.
class HTMLCSSScriptNoStatePrefetchBrowserTest
: public NoStatePrefetchBrowserTest {
public:
void SetUp() override {
std::map<std::string, std::string> parameters;
parameters["skip_other"] = "true";
parameters["skip_async_script"] = "false";
feature_list_.InitWithFeaturesAndParameters(
{{blink::features::kLightweightNoStatePrefetch, parameters}}, {});
NoStatePrefetchBrowserTest::SetUp();
}
private:
base::test::ScopedFeatureList feature_list_;
};
// Checks that the expected resource types are fetched via NoState Prefetch.
IN_PROC_BROWSER_TEST_F(HTMLCSSScriptNoStatePrefetchBrowserTest,
PrefetchHTMLCSSScript) {
std::unique_ptr<TestPrerender> test_prerender =
PrefetchFromFile(kPrefetchPageMultipleResourceTypes,
FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
// Verify that the page load did not happen.
test_prerender->WaitForLoads(0);
WaitForRequestCount(src_server()->GetURL(kPrefetchPageMultipleResourceTypes),
1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchPng), 0);
WaitForRequestCount(src_server()->GetURL(kPrefetchCss), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchFont), 0);
}
void GetCookieCallback(base::RepeatingClosure callback,
const net::CookieAccessResultList& cookie_list,
const net::CookieAccessResultList& excluded_cookies) {
bool found_chocolate = false;
bool found_oatmeal = false;
for (const auto& c : cookie_list) {
if (c.cookie.Name() == "chocolate-chip") {
EXPECT_EQ("the-best", c.cookie.Value());
found_chocolate = true;
}
if (c.cookie.Name() == "oatmeal") {
EXPECT_EQ("sublime", c.cookie.Value());
found_oatmeal = true;
}
}
CHECK(found_chocolate && found_oatmeal);
callback.Run();
}
// Check cookie loading for prefetched pages.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchCookie) {
GURL url = src_server()->GetURL(kPrefetchCookiePage);
std::unique_ptr<TestPrerender> test_prerender =
PrefetchFromURL(url, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
content::StoragePartition* storage_partition =
content::BrowserContext::GetStoragePartitionForSite(
current_browser()->profile(), url, false);
net::CookieOptions options = net::CookieOptions::MakeAllInclusive();
base::RunLoop loop;
storage_partition->GetCookieManagerForBrowserProcess()->GetCookieList(
url, options, base::BindOnce(GetCookieCallback, loop.QuitClosure()));
loop.Run();
}
// Check cookie loading for a cross-domain prefetched pages.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchCookieCrossDomain) {
static const std::string secondary_domain = "www.foo.com";
GURL cross_domain_url(base::StringPrintf(
"http://%s:%d%s", secondary_domain.c_str(),
embedded_test_server()->host_port_pair().port(), kPrefetchCookiePage));
std::unique_ptr<TestPrerender> test_prerender =
PrefetchFromURL(cross_domain_url, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
// While the request is cross-site, it's permitted to set (implicitly) lax
// cookies on a cross-site navigation.
content::StoragePartition* storage_partition =
content::BrowserContext::GetStoragePartitionForSite(
current_browser()->profile(), cross_domain_url, false);
net::CookieOptions options = net::CookieOptions::MakeAllInclusive();
base::RunLoop loop;
storage_partition->GetCookieManagerForBrowserProcess()->GetCookieList(
cross_domain_url, options,
base::BindOnce(GetCookieCallback, loop.QuitClosure()));
loop.Run();
}
// Check cookie loading for a cross-domain prefetched pages.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest,
PrefetchCookieCrossDomainSameSiteStrict) {
constexpr char kSecondaryDomain[] = "www.foo.com";
GURL cross_domain_url =
embedded_test_server()->GetURL(kSecondaryDomain, "/echoall");
EXPECT_TRUE(SetCookie(current_browser()->profile(), cross_domain_url,
"cookie_A=A; SameSite=Strict;"));
EXPECT_TRUE(SetCookie(current_browser()->profile(), cross_domain_url,
"cookie_B=B; SameSite=Lax;"));
std::unique_ptr<TestPrerender> test_prerender =
PrefetchFromURL(cross_domain_url, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
ui_test_utils::NavigateToURL(current_browser(), cross_domain_url);
EXPECT_TRUE(WaitForLoadStop(
current_browser()->tab_strip_model()->GetActiveWebContents()));
std::string html_content;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
current_browser()->tab_strip_model()->GetActiveWebContents(),
"domAutomationController.send(document.body.innerHTML)", &html_content));
// For any cross origin navigation (including prerender), SameSite Strict
// cookies should not be sent, but Lax should.
EXPECT_EQ(std::string::npos, html_content.find("cookie_A=A"));
EXPECT_NE(std::string::npos, html_content.find("cookie_B=B"));
}
// Check cookie loading for a same-domain prefetched pages.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest,
PrefetchCookieSameDomainSameSiteStrict) {
GURL same_domain_url = embedded_test_server()->GetURL("/echoall");
EXPECT_TRUE(SetCookie(current_browser()->profile(), same_domain_url,
"cookie_A=A; SameSite=Strict;"));
EXPECT_TRUE(SetCookie(current_browser()->profile(), same_domain_url,
"cookie_B=B; SameSite=Lax;"));
std::unique_ptr<TestPrerender> test_prerender =
PrefetchFromURL(same_domain_url, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
ui_test_utils::NavigateToURL(current_browser(), same_domain_url);
EXPECT_TRUE(WaitForLoadStop(
current_browser()->tab_strip_model()->GetActiveWebContents()));
std::string html_content;
EXPECT_TRUE(content::ExecuteScriptAndExtractString(
current_browser()->tab_strip_model()->GetActiveWebContents(),
"domAutomationController.send(document.body.innerHTML)", &html_content));
// For any same origin navigation (including prerender), SameSite Strict
// cookies should not be sent, but Lax should.
EXPECT_NE(std::string::npos, html_content.find("cookie_A=A"));
EXPECT_NE(std::string::npos, html_content.find("cookie_B=B"));
}
// Check that the LOAD_PREFETCH flag is set.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchLoadFlag) {
GURL prefetch_page = src_server()->GetURL(kPrefetchPage);
GURL prefetch_script = src_server()->GetURL(kPrefetchScript);
content::URLLoaderMonitor monitor({prefetch_page, prefetch_script});
std::unique_ptr<TestPrerender> test_prerender =
PrefetchFromFile(kPrefetchPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForRequestCount(prefetch_page, 1);
WaitForRequestCount(prefetch_script, 1);
// Verify that the page load did not happen.
test_prerender->WaitForLoads(0);
monitor.WaitForUrls();
base::Optional<network::ResourceRequest> page_request =
monitor.GetRequestInfo(prefetch_page);
EXPECT_TRUE(page_request->load_flags & net::LOAD_PREFETCH);
base::Optional<network::ResourceRequest> script_request =
monitor.GetRequestInfo(prefetch_script);
EXPECT_TRUE(script_request->load_flags & net::LOAD_PREFETCH);
}
// Check that prefetched resources and subresources set the 'Purpose: prefetch'
// header.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PurposeHeaderIsSet) {
GURL prefetch_page = src_server()->GetURL(kPrefetchPage);
GURL prefetch_script = src_server()->GetURL(kPrefetchScript);
content::URLLoaderMonitor monitor({prefetch_page, prefetch_script});
std::unique_ptr<TestPrerender> test_prerender =
PrefetchFromFile(kPrefetchPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForRequestCount(prefetch_page, 1);
WaitForRequestCount(prefetch_script, 1);
monitor.WaitForUrls();
for (const GURL& url : {prefetch_page, prefetch_script}) {
base::Optional<network::ResourceRequest> request =
monitor.GetRequestInfo(url);
EXPECT_TRUE(request->load_flags & net::LOAD_PREFETCH);
EXPECT_FALSE(request->headers.HasHeader(kExpectedPurposeHeaderOnPrefetch));
EXPECT_TRUE(request->cors_exempt_headers.HasHeader(
kExpectedPurposeHeaderOnPrefetch));
std::string purpose_header;
request->cors_exempt_headers.GetHeader(kExpectedPurposeHeaderOnPrefetch,
&purpose_header);
EXPECT_EQ("prefetch", purpose_header);
}
}
// Check that on normal navigations the 'Purpose: prefetch' header is not set.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest,
PurposeHeaderNotSetWhenNotPrefetching) {
GURL prefetch_page = src_server()->GetURL(kPrefetchPage);
GURL prefetch_script = src_server()->GetURL(kPrefetchScript);
GURL prefetch_script2 = src_server()->GetURL(kPrefetchScript2);
content::URLLoaderMonitor monitor(
{prefetch_page, prefetch_script, prefetch_script2});
ui_test_utils::NavigateToURL(current_browser(), prefetch_page);
WaitForRequestCount(prefetch_page, 1);
WaitForRequestCount(prefetch_script, 1);
WaitForRequestCount(prefetch_script2, 1);
monitor.WaitForUrls();
for (const GURL& url : {prefetch_page, prefetch_script, prefetch_script2}) {
base::Optional<network::ResourceRequest> request =
monitor.GetRequestInfo(url);
EXPECT_FALSE(request->load_flags & net::LOAD_PREFETCH);
EXPECT_FALSE(request->headers.HasHeader(kExpectedPurposeHeaderOnPrefetch));
EXPECT_FALSE(request->cors_exempt_headers.HasHeader(
kExpectedPurposeHeaderOnPrefetch));
}
}
// Check that a prefetch followed by a load produces the appropriate
// histograms. Note that other histogram testing is done in
// browser/page_load_metrics, in particular, testing the combinations of
// Warm/Cold and Cacheable/NoCacheable.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchHistograms) {
PrefetchFromFile(kPrefetchPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
histogram_tester().ExpectTotalCount(
"Prerender.websame_PrefetchTTFCP.Warm.Cacheable.Visible", 0);
test_utils::FirstContentfulPaintManagerWaiter* fcp_waiter =
test_utils::FirstContentfulPaintManagerWaiter::Create(
GetPrerenderManager());
ui_test_utils::NavigateToURL(current_browser(),
src_server()->GetURL(kPrefetchPage));
fcp_waiter->Wait();
histogram_tester().ExpectTotalCount(
"Prerender.websame_PrefetchTTFCP.Warm.Cacheable.Visible", 1);
histogram_tester().ExpectTotalCount(
"Prerender.websame_NoStatePrefetchResponseTypes", 2);
histogram_tester().ExpectTotalCount("Prerender.websame_PrefetchAge", 1);
}
// Checks the prefetch of an img tag.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchImage) {
GURL main_page_url = GetURLWithReplacement(
kPrefetchImagePage, "REPLACE_WITH_IMAGE_URL", kPrefetchJpeg);
PrefetchFromURL(main_page_url, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForRequestCount(src_server()->GetURL(kPrefetchJpeg), 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";
GURL cross_domain_url(base::StringPrintf(
"http://%s:%d%s", secondary_domain.c_str(),
embedded_test_server()->host_port_pair().port(), kPrefetchPage));
PrefetchFromURL(cross_domain_url, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForRequestCount(src_server()->GetURL(kPrefetchPage), 1);
}
// Checks that prefetching from a cross-domain subframe works correctly.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest,
PrefetchFromCrossDomainSubframe) {
const std::string secondary_domain = "www.foo.com";
GURL target_url(base::StringPrintf(
"http://%s:%d%s", secondary_domain.c_str(),
embedded_test_server()->host_port_pair().port(), kPrefetchPage));
GURL inner_frame_url = ServeLoaderURLWithHostname(
kPrefetchLoaderPath, "REPLACE_WITH_PREFETCH_URL", target_url, "",
secondary_domain);
GURL outer_frame_url = ServeLoaderURL(
kPrefetchFromSubframe, "REPLACE_WITH_SUBFRAME_URL", inner_frame_url, "");
std::vector<FinalStatus> expected_final_status_queue(
1, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
std::vector<std::unique_ptr<TestPrerender>> prerenders =
NavigateWithPrerenders(outer_frame_url, expected_final_status_queue);
prerenders[0]->WaitForStop();
WaitForRequestCount(src_server()->GetURL(kPrefetchPage), 1);
}
// Checks that response header CSP is respected.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, ResponseHeaderCSP) {
static const std::string secondary_domain = "foo.bar";
GURL second_script_url(std::string("http://foo.bar") + kPrefetchScript2);
GURL prefetch_response_header_csp = GetURLWithReplacement(
kPrefetchResponseHeaderCSP, "REPLACE_WITH_PORT",
base::NumberToString(src_server()->host_port_pair().port()));
PrefetchFromURL(prefetch_response_header_csp,
FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
// The second script is in the correct domain for CSP, but the first script is
// not.
WaitForRequestCount(prefetch_response_header_csp, 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 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";
GURL second_script_url(std::string("http://foo.bar") + kPrefetchScript2);
GURL prefetch_meta_tag_csp = GetURLWithReplacement(
kPrefetchMetaCSP, "REPLACE_WITH_PORT",
base::NumberToString(src_server()->host_port_pair().port()));
PrefetchFromURL(prefetch_meta_tag_csp,
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.
WaitForRequestCount(prefetch_meta_tag_csp, 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 0);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 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) {
PrefetchFromFile(kPrefetchPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
PrefetchFromFile(kPrefetchPage2, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForRequestCount(src_server()->GetURL(kPrefetchPage), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchPage2), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 1);
}
// Checks that a second prefetch request, started before the first stops,
// succeeds.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchSimultaneous) {
GURL first_url = src_server()->GetURL("/hung");
// 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);
WaitForRequestCount(src_server()->GetURL(kPrefetchPage2), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 1);
}
// Checks that a prefetch does not recursively prefetch.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, NoPrefetchRecursive) {
// A prefetch of a page with a prefetch of the image page should not load the
// image page.
PrefetchFromFile(kPrefetchRecursePage,
FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForRequestCount(src_server()->GetURL(kPrefetchRecursePage), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchNostorePage), 0);
// When the first page is loaded, the image page should be prefetched. The
// test may finish before the prerender is torn down, so
// IgnorePrerenderContents() is called to skip the final status check.
prerender_contents_factory()->IgnorePrerenderContents();
ui_test_utils::NavigateToURL(current_browser(),
src_server()->GetURL(kPrefetchRecursePage));
WaitForRequestCount(src_server()->GetURL(kPrefetchNostorePage), 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) {
PrefetchFromFile(CreateServerRedirect(kPrefetchPage),
FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 1);
}
// Checks that non-HTTP(S) main resource redirects are marked as unsupported
// scheme.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest,
PrefetchRedirectUnsupportedScheme) {
PrefetchFromFile(
CreateServerRedirect("invalidscheme://www.google.com/test.html"),
FINAL_STATUS_UNSUPPORTED_SCHEME, 1);
}
// Checks that a 302 redirect is followed.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, Prefetch302Redirect) {
PrefetchFromFile(k302RedirectPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 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 = CreateServerRedirect(kPrefetchPage);
GURL redirect_url = src_server()->GetURL(redirect_path);
GURL page_url = src_server()->GetURL(kPrefetchPage);
content::URLLoaderMonitor monitor({redirect_url});
PrefetchFromFile(redirect_path, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForRequestCount(redirect_url, 1);
WaitForRequestCount(page_url, 1);
monitor.WaitForUrls();
base::Optional<network::ResourceRequest> request =
monitor.GetRequestInfo(redirect_url);
EXPECT_TRUE(request->load_flags & net::LOAD_PREFETCH);
}
// Checks that a subresource 301 redirect is followed.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, Prefetch301Subresource) {
PrefetchFromFile(kPrefetchSubresourceRedirectPage,
FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 1);
}
// Checks a client redirect is not followed.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchClientRedirect) {
PrefetchFromFile(
"/client-redirect/?" + net::EscapeQueryParamValue(kPrefetchPage, false),
FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
ui_test_utils::NavigateToURL(current_browser(),
src_server()->GetURL(kPrefetchPage2));
// A complete load of kPrefetchPage2 is used as a sentinel. Otherwise the test
// ends before script_counter would reliably see the load of kPrefetchScript,
// were it to happen.
WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 0);
}
// Prefetches a page that contains an automatic download triggered through an
// iframe. The request to download should not reach the server.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchDownloadIframe) {
PrefetchFromFile("/prerender/prerender_download_iframe.html",
FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
// A complete load of kPrefetchPage2 is used as a sentinel as in test
// |PrefetchClientRedirect| above.
WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchDownloadFile), 0);
}
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest,
PrefetchDownloadViaClientRedirect) {
PrefetchFromFile("/prerender/prerender_download_refresh.html",
FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
// A complete load of kPrefetchPage2 is used as a sentinel as in test
// |PrefetchClientRedirect| above.
WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchDownloadFile), 0);
}
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchPageWithFragment) {
std::unique_ptr<TestPrerender> test_prerender = PrefetchFromFile(
kPrefetchPageWithFragment, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
test_prerender->WaitForLoads(0);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 0);
}
// Checks that a prefetch of a CRX will result in a cancellation due to
// download.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchCrx) {
PrefetchFromFile("/prerender/extension.crx", FINAL_STATUS_DOWNLOAD);
}
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrefetchHttps) {
UseHttpsSrcServer();
PrefetchFromFile(kPrefetchPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForRequestCount(src_server()->GetURL(kPrefetchPage), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 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(GetChromeTestDataDir());
ASSERT_TRUE(https_server.Start());
std::unique_ptr<TestPrerender> prerender = PrefetchFromURL(
https_server.GetURL(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(GetChromeTestDataDir());
ASSERT_TRUE(https_server.Start());
GURL https_url = https_server.GetURL("/prerender/image.jpeg");
GURL main_page_url = GetURLWithReplacement(
kPrefetchImagePage, "REPLACE_WITH_IMAGE_URL", https_url.spec());
std::unique_ptr<TestPrerender> prerender =
PrefetchFromURL(main_page_url, 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.
WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 1);
}
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, Loop) {
std::unique_ptr<TestPrerender> test_prerender = PrefetchFromFile(
kPrefetchLoopPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForRequestCount(src_server()->GetURL(kPrefetchLoopPage), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 1);
}
// Flaky timeouts on Win7 Tests; see https://crbug.com/985255.
#if defined(OS_WIN)
#define MAYBE_RendererCrash DISABLED_RendererCrash
#else
#define MAYBE_RendererCrash RendererCrash
#endif
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, MAYBE_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);
content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
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) {
PrefetchFromFile(kPrefetchPng, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForRequestCount(src_server()->GetURL(kPrefetchPng), 1);
}
// Checks that the prefetch of jpeg correctly loads the jpeg.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, Jpeg) {
PrefetchFromFile(kPrefetchJpeg, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForRequestCount(src_server()->GetURL(kPrefetchJpeg), 1);
}
// If the main resource is unsafe, the whole prefetch is cancelled.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest,
PrerenderSafeBrowsingTopLevel) {
GURL url = src_server()->GetURL(kPrefetchPage);
GetFakeSafeBrowsingDatabaseManager()->SetThreatTypeForUrl(
url, safe_browsing::SB_THREAT_TYPE_URL_MALWARE);
std::unique_ptr<TestPrerender> prerender =
PrefetchFromFile(kPrefetchPage, FINAL_STATUS_SAFE_BROWSING);
// The frame request may have been started, but SafeBrowsing must have already
// blocked it. Verify that the page load did not happen.
prerender->WaitForLoads(0);
// The frame resource has been blocked by SafeBrowsing, the subresource on
// the page shouldn't be requested at all.
WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 0);
}
// If a subresource is unsafe, the corresponding request is cancelled.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest,
PrerenderSafeBrowsingSubresource) {
GURL url = src_server()->GetURL(kPrefetchScript);
GetFakeSafeBrowsingDatabaseManager()->SetThreatTypeForUrl(
url, safe_browsing::SB_THREAT_TYPE_URL_MALWARE);
constexpr char kPrefetchCanceledHistogram[] =
"SB2Test.ResourceTypes2.UnsafePrefetchCanceled";
base::RunLoop run_loop;
bool prefetch_canceled_histogram_added = false;
EXPECT_TRUE(base::StatisticsRecorder::SetCallback(
kPrefetchCanceledHistogram,
base::Bind(
[](const base::Closure& quit_closure, bool* called,
base::HistogramBase::Sample sample) {
*called = true;
quit_closure.Run();
},
run_loop.QuitClosure(), &prefetch_canceled_histogram_added)));
std::unique_ptr<TestPrerender> prerender =
PrefetchFromFile(kPrefetchPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
// The frame resource was loaded.
WaitForRequestCount(src_server()->GetURL(kPrefetchPage), 1);
// There should be a histogram sample recorded for SafeBrowsing canceling an
// unsafe prefetch, which corresponded to the subresource.
run_loop.Run();
EXPECT_TRUE(prefetch_canceled_histogram_added);
base::StatisticsRecorder::ClearCallback(kPrefetchCanceledHistogram);
}
// 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(kPrefetchPage);
PrefetchFromFile(kPrefetchPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForHistoryBackendToRun(profile);
// Navigate to another page.
GURL navigated_url = src_server()->GetURL(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(base::Contains(urls, navigated_url));
// Check that the URL that was prefetched is not in history.
EXPECT_FALSE(base::Contains(urls, prefetched_url));
// The loader URL is the remaining entry.
EXPECT_EQ(2U, urls.size());
}
// Checks that prefetch requests have net::IDLE priority.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, IssuesIdlePriorityRequests) {
// TODO(pasko): Figure out how to test that while a prefetched URL is in IDLE
// priority state, a high-priority request with the same URL from a foreground
// navigation hits the server.
GURL script_url = src_server()->GetURL(kPrefetchScript);
content::URLLoaderMonitor monitor({script_url});
PrefetchFromFile(kPrefetchPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForRequestCount(script_url, 1);
monitor.WaitForUrls();
#if defined(OS_ANDROID)
// On Android requests from prerenders do not get downgraded
// priority. See: https://crbug.com/652746.
constexpr net::RequestPriority kExpectedPriority = net::HIGHEST;
#else
constexpr net::RequestPriority kExpectedPriority = net::IDLE;
#endif
base::Optional<network::ResourceRequest> request =
monitor.GetRequestInfo(script_url);
EXPECT_EQ(kExpectedPriority, request->priority);
}
// Checks that a registered ServiceWorker (SW) that is not currently running
// will intercepts a prefetch request.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, ServiceWorkerIntercept) {
// Register and launch a SW.
base::string16 expected_title = base::ASCIIToUTF16("SW READY");
content::TitleWatcher title_watcher(GetActiveWebContents(), expected_title);
ui_test_utils::NavigateToURL(current_browser(),
src_server()->GetURL(kServiceWorkerLoader));
EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
// Stop any SW, killing the render process in order to test that the
// lightweight renderer created for NoState prefetch does not interfere with
// SW startup.
int host_count = 0;
for (content::RenderProcessHost::iterator iter(
content::RenderProcessHost::AllHostsIterator());
!iter.IsAtEnd(); iter.Advance()) {
// Don't count spare RenderProcessHosts.
if (!iter.GetCurrentValue()->HostHasNotBeenUsed())
++host_count;
content::RenderProcessHostWatcher process_exit_observer(
iter.GetCurrentValue(),
content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
// TODO(wez): This used to use wait=true.
iter.GetCurrentValue()->Shutdown(content::RESULT_CODE_KILLED);
process_exit_observer.Wait();
}
// There should be at most one render_process_host, that created for the SW.
EXPECT_EQ(1, host_count);
// Open a new tab to replace the one closed with all the RenderProcessHosts.
ui_test_utils::NavigateToURLWithDisposition(
current_browser(), GURL(url::kAboutBlankURL),
WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
// The SW intercepts kPrefetchPage and replaces it with a body that contains
// an <img> tage for kPrefetchPng. This verifies that the SW ran correctly by
// observing the fetch of the image.
PrefetchFromFile(kPrefetchPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForRequestCount(src_server()->GetURL(kPrefetchPng), 1);
}
// Checks that prefetching happens if an appcache is mentioned in the html tag
// but is uninitialized.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, AppCacheHtmlUninitialized) {
PrefetchFromFile(kPrefetchAppcache, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForRequestCount(src_server()->GetURL(kPrefetchPng), 1);
}
// Checks that prefetching does not if an initialized appcache is mentioned in
// the html tag.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, AppCacheHtmlInitialized) {
base::TimeTicks current_time = GetPrerenderManager()->GetCurrentTimeTicks();
OverridePrerenderManagerTimeTicks();
// Some navigations have already occurred in test setup. In order to track
// duplicate prefetches correctly the test clock needs to be beyond those
// navigations.
clock_.SetNowTicks(current_time);
clock_.Advance(base::TimeDelta::FromSeconds(600));
std::string origin = "http://127.0.0.1:8080";
GURL image_url(origin + kPrefetchPng);
GURL manifest_url(origin + kPrefetchAppcacheManifest);
GURL appcache_page_url(origin + kPrefetchAppcache);
GURL script_url(origin + kPrefetchScript);
base::RunLoop run_loop;
std::unique_ptr<content::URLLoaderInterceptor> url_loader_interceptor =
content::URLLoaderInterceptor::ServeFilesFromDirectoryAtOrigin(
"chrome/test/data", GURL(origin),
base::BindRepeating(base::BindLambdaForTesting([&](const GURL& url) {
if (url == script_url)
run_loop.Quit();
})));
// Load the page into the appcache.
ui_test_utils::NavigateToURL(current_browser(), appcache_page_url);
WaitForAppcache(manifest_url);
// If a page is prefetch shortly after being loading, the prefetch is
// canceled. Advancing the clock prevents the cancelation.
clock_.Advance(base::TimeDelta::FromSeconds(6000));
// While the prefetch stops when it sees the AppCache manifest, from the point
// of view of the prerender manager the prefetch stops normally.
PrefetchFromURL(appcache_page_url, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
// The prefetch should have been canceled before the script in
// kPrefetchAppcache is loaded (note the script is not mentioned in the
// manifest).
run_loop.Run();
}
// If a page has been cached by another AppCache, the prefetch should be
// canceled.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, AppCacheRegistered) {
base::TimeTicks current_time = GetPrerenderManager()->GetCurrentTimeTicks();
OverridePrerenderManagerTimeTicks();
// Some navigations have already occurred in test setup. In order to track
// duplicate prefetches correctly the test clock needs to be beyond those
// navigations.
clock_.SetNowTicks(current_time);
clock_.Advance(base::TimeDelta::FromSeconds(600));
std::string origin = "http://127.0.0.1:8080";
// This manifest lists kPrefetchPage, but does not explicitly
// list a manifest itself.
GURL manifest_url(origin + kPrefetchAppcacheManifest);
GURL appcache_page_url(origin + kPrefetchAppcache);
GURL prefetch_page_url(origin + kPrefetchPage);
GURL script_url(origin + kPrefetchScript);
bool seen_page_url = false;
bool seen_script_url = false;
base::RunLoop run_loop;
std::unique_ptr<content::URLLoaderInterceptor> url_loader_interceptor =
content::URLLoaderInterceptor::ServeFilesFromDirectoryAtOrigin(
"chrome/test/data", GURL(origin),
base::BindRepeating(base::BindLambdaForTesting([&](const GURL& url) {
if (url == script_url)
seen_script_url = true;
if (url == prefetch_page_url)
seen_page_url = true;
if (seen_script_url && seen_page_url)
run_loop.Quit();
})));
// Load the page into the appcache, and then the prefetch page so it can be
// cached. After each navigation, wait for the appcache to catch up. This
// avoids timeouts which for an unknown reason occur if the Appcache is
// queried only after both navitations.
ui_test_utils::NavigateToURL(current_browser(), appcache_page_url);
WaitForAppcache(manifest_url);
ui_test_utils::NavigateToURL(current_browser(), prefetch_page_url);
WaitForAppcache(manifest_url);
// If a page is prefetch shortly after being loading, the prefetch is
// canceled. Advancing the clock prevents the cancelation.
clock_.Advance(base::TimeDelta::FromSeconds(6000));
PrefetchFromURL(prefetch_page_url, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
// Neither the page nor the script should be prefetched, so
// wait until we see both.
run_loop.Run();
}
class NoStatePrefetchIncognitoBrowserTest : public NoStatePrefetchBrowserTest {
public:
void SetUpOnMainThread() override {
Profile* normal_profile = current_browser()->profile();
set_browser(OpenURLOffTheRecord(normal_profile, GURL("about:blank")));
NoStatePrefetchBrowserTest::SetUpOnMainThread();
current_browser()->profile()->GetPrefs()->SetInteger(
prefs::kCookieControlsMode,
static_cast<int>(content_settings::CookieControlsMode::kOff));
}
};
// Checks that prerendering works in incognito mode.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchIncognitoBrowserTest,
PrerenderIncognito) {
std::unique_ptr<TestPrerender> test_prerender =
PrefetchFromFile(kPrefetchPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
// Verify that the page load did not happen.
test_prerender->WaitForLoads(0);
WaitForRequestCount(src_server()->GetURL(kPrefetchPage), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript2), 0);
}
// Checks that when the history is cleared, NoStatePrefetch history is cleared.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, ClearHistory) {
std::unique_ptr<TestPrerender> test_prerender = PrefetchFromFile(
kHungPrerenderPage, FINAL_STATUS_CACHE_OR_HISTORY_CLEARED);
ClearBrowsingData(current_browser(),
ChromeBrowsingDataRemoverDelegate::DATA_TYPE_HISTORY);
test_prerender->WaitForStop();
// Make sure prerender history was cleared.
EXPECT_EQ(0U, GetHistoryLength());
}
// Checks that when the cache is cleared, NoStatePrefetch history is not
// cleared.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, ClearCache) {
std::unique_ptr<TestPrerender> prerender = PrefetchFromFile(
kHungPrerenderPage, FINAL_STATUS_CACHE_OR_HISTORY_CLEARED);
ClearBrowsingData(current_browser(),
content::BrowsingDataRemover::DATA_TYPE_CACHE);
prerender->WaitForStop();
// Make sure prerender history was not cleared. Not a vital behavior, but
// used to compare with ClearHistory test.
EXPECT_EQ(1U, GetHistoryLength());
}
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, CancelAll) {
GURL url = src_server()->GetURL(kHungPrerenderPage);
std::unique_ptr<TestPrerender> prerender =
PrefetchFromURL(url, FINAL_STATUS_CANCELLED, 0);
GetPrerenderManager()->CancelAllPrerenders();
prerender->WaitForStop();
EXPECT_FALSE(prerender->contents());
}
// Cancels the prerender of a page with its own prerender. The second prerender
// should never be started.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest,
CancelPrerenderWithPrerender) {
GURL url = src_server()->GetURL("/prerender/prerender_infinite_a.html");
std::unique_ptr<TestPrerender> prerender =
PrefetchFromURL(url, FINAL_STATUS_CANCELLED);
GetPrerenderManager()->CancelAllPrerenders();
prerender->WaitForStop();
EXPECT_FALSE(prerender->contents());
}
// Checks shutdown code while a prerender is active.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrerenderQuickQuit) {
DisableLoadEventCheck();
GURL url = src_server()->GetURL(kHungPrerenderPage);
std::unique_ptr<TestPrerender> prerender =
PrefetchFromURL(url, FINAL_STATUS_APP_TERMINATING);
}
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrerenderClickNewWindow) {
GURL url = src_server()->GetURL("/prerender/prerender_page_with_link.html");
std::unique_ptr<TestPrerender> prerender =
PrefetchFromURL(url, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
OpenDestURLViaClickNewWindow(url);
}
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest,
PrerenderClickNewForegroundTab) {
GURL url = src_server()->GetURL("/prerender/prerender_page_with_link.html");
std::unique_ptr<TestPrerender> prerender =
PrefetchFromURL(url, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
OpenDestURLViaClickNewForegroundTab(url);
}
// Checks that renderers using excessive memory will be terminated.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrerenderExcessiveMemory) {
ASSERT_TRUE(GetPrerenderManager());
GetPrerenderManager()->mutable_config().max_bytes = 100;
// The excessive memory kill may happen before or after the load event as it
// happens asynchronously with IPC calls. Even if the test does not start
// allocating until after load, the browser process might notice before the
// message gets through. This happens on XP debug bots because they're so
// slow. Instead, don't bother checking the load event count.
DisableLoadEventCheck();
PrefetchFromURL(
src_server()->GetURL("/prerender/prerender_excessive_memory.html"),
FINAL_STATUS_MEMORY_LIMIT_EXCEEDED);
}
} // namespace prerender