blob: 0fe429abc0336d8314e1b24bf1fab69faf14775b [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 <string>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/metrics/statistics_recorder.h"
#include "base/run_loop.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.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_constants.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/history/history_test_utils.h"
#include "chrome/browser/predictors/autocomplete_action_predictor.h"
#include "chrome/browser/predictors/autocomplete_action_predictor_factory.h"
#include "chrome/browser/prefetch/no_state_prefetch/no_state_prefetch_manager_factory.h"
#include "chrome/browser/prefetch/no_state_prefetch/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/browser_window.h"
#include "chrome/browser/ui/location_bar/location_bar.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/no_state_prefetch/browser/no_state_prefetch_handle.h"
#include "components/no_state_prefetch/browser/no_state_prefetch_manager.h"
#include "components/omnibox/browser/omnibox_edit_model.h"
#include "components/omnibox/browser/omnibox_view.h"
#include "components/prefs/pref_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/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/result_codes.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/back_forward_cache_util.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/chrome_debug_urls.h"
#include "third_party/blink/public/common/features.h"
using prerender::test_utils::DestructionWaiter;
using prerender::test_utils::TestPrerender;
using task_manager::browsertest_util::WaitForTaskManagerRows;
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 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:
raw_ptr<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(const NewTabNavigationOrSwapObserver&) =
delete;
NewTabNavigationOrSwapObserver& operator=(
const NewTabNavigationOrSwapObserver&) = delete;
~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_;
};
class NoStatePrefetchBrowserTest
: public test_utils::PrerenderInProcessBrowserTest {
public:
NoStatePrefetchBrowserTest() {}
NoStatePrefetchBrowserTest(const NoStatePrefetchBrowserTest&) = delete;
NoStatePrefetchBrowserTest& operator=(const NoStatePrefetchBrowserTest&) =
delete;
void SetUpDefaultCommandLine(base::CommandLine* command_line) override {
test_utils::PrerenderInProcessBrowserTest::SetUpDefaultCommandLine(
command_line);
command_line->AppendSwitchASCII(embedder_support::kOriginTrialPublicKey,
kOriginTrialPublicKeyForTesting);
command_line->AppendSwitchASCII(switches::kEnableBlinkFeatures,
"SpeculationRulesPrefetchProxy");
}
void SetUpOnMainThread() override {
test_utils::PrerenderInProcessBrowserTest::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
}
void OverrideNoStatePrefetchManagerTimeTicks() {
// The default zero time causes the prerender manager to do strange things.
clock_.Advance(base::Seconds(1));
GetNoStatePrefetchManager()->SetTickClockForTesting(&clock_);
}
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);
if (ShouldAbortPrerenderBeforeSwap(expected_final_status_queue.front())) {
// The prefetcher will abort on its own. Assert it does so correctly.
prerenders[0]->WaitForStop();
EXPECT_FALSE(prerenders[0]->contents());
} else {
// Otherwise, check that it prefetched correctly.
test_utils::TestNoStatePrefetchContents* no_state_prefetch_contents =
prerenders[0]->contents();
if (no_state_prefetch_contents) {
EXPECT_EQ(FINAL_STATUS_UNKNOWN,
no_state_prefetch_contents->final_status());
}
}
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;
}
}
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 |no_state_prefetch_manager_|'s history, or SIZE_MAX on
// failure.
size_t GetHistoryLength() const {
std::unique_ptr<base::DictionaryValue> prerender_dict =
GetNoStatePrefetchManager()->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->GetList().size();
}
// Clears the specified data using BrowsingDataRemover.
void ClearBrowsingData(Browser* browser, uint64_t remove_mask) {
content::BrowsingDataRemover* remover =
browser->profile()->GetBrowsingDataRemover();
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.
}
// 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 BUILDFLAG(IS_MAC)
OpenURLWithJSImpl("MetaShiftClick", dest_url, GURL(), true);
#else
OpenURLWithJSImpl("CtrlShiftClick", dest_url, GURL(), true);
#endif
}
base::SimpleTestTickClock clock_;
private:
base::test::ScopedFeatureList feature_list_;
};
class NoStatePrefetchBrowserTestHttpCache
: public NoStatePrefetchBrowserTest,
public testing::WithParamInterface<bool> {
protected:
void SetUp() override {
bool split_cache_by_network_isolation_key = GetParam();
if (split_cache_by_network_isolation_key) {
feature_list_.InitAndEnableFeature(
net::features::kSplitCacheByNetworkIsolationKey);
} else {
feature_list_.InitAndDisableFeature(
net::features::kSplitCacheByNetworkIsolationKey);
}
NoStatePrefetchBrowserTest::SetUp();
}
private:
base::test::ScopedFeatureList feature_list_;
};
using NoStatePrefetchBrowserTestHttpCache_DefaultAndAppendFrameOrigin =
NoStatePrefetchBrowserTestHttpCache;
// Test that the network isolation key is correctly populated during a prefetch.
IN_PROC_BROWSER_TEST_P(
NoStatePrefetchBrowserTestHttpCache_DefaultAndAppendFrameOrigin,
PrefetchTwoCrossOriginFrames) {
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);
ASSERT_TRUE(ui_test_utils::NavigateToURL(
current_browser(), src_server()->GetURL(prerender_path)));
WaitForRequestCount(image_src, 2);
}
INSTANTIATE_TEST_SUITE_P(
All,
NoStatePrefetchBrowserTestHttpCache_DefaultAndAppendFrameOrigin,
::testing::Values(true));
// 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);
}
ASSERT_TRUE(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);
ASSERT_TRUE(
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::Bool());
// 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 =
current_browser()->profile()->GetStoragePartitionForUrl(url, false);
net::CookieOptions options = net::CookieOptions::MakeAllInclusive();
base::RunLoop loop;
storage_partition->GetCookieManagerForBrowserProcess()->GetCookieList(
url, options, net::CookiePartitionKeyCollection(),
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 =
current_browser()->profile()->GetStoragePartitionForUrl(cross_domain_url,
false);
net::CookieOptions options = net::CookieOptions::MakeAllInclusive();
base::RunLoop loop;
storage_partition->GetCookieManagerForBrowserProcess()->GetCookieList(
cross_domain_url, options, net::CookiePartitionKeyCollection(),
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);
ASSERT_TRUE(
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);
ASSERT_TRUE(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();
absl::optional<network::ResourceRequest> page_request =
monitor.GetRequestInfo(prefetch_page);
EXPECT_TRUE(page_request->load_flags & net::LOAD_PREFETCH);
absl::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}) {
absl::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});
ASSERT_TRUE(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}) {
absl::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));
}
}
// 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) {
// The test assumes the previous page gets deleted after navigation. Disable
// back/forward cache to ensure that it doesn't get preserved in the cache.
content::DisableBackForwardCacheForTesting(
GetActiveWebContents(),
content::BackForwardCache::TEST_ASSUMES_NO_CACHING);
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 prefetcher is torn down, so
// IgnoreNoStatePrefetchContents() is called to skip the final status check.
no_state_prefetch_contents_factory()->IgnoreNoStatePrefetchContents();
ASSERT_TRUE(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();
absl::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);
ASSERT_TRUE(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);
}
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, RendererCrash) {
// Navigate to about:blank to get the session storage namespace.
ASSERT_TRUE(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 =
no_state_prefetch_contents_factory()->ExpectNoStatePrefetchContents(
FINAL_STATUS_RENDERER_CRASHED);
content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
std::unique_ptr<NoStatePrefetchHandle> no_state_prefetch_handle(
GetNoStatePrefetchManager()->StartPrefetchingFromExternalRequest(
GURL(blink::kChromeUICrashURL), content::Referrer(),
storage_namespace, gfx::Rect(kSize)));
ASSERT_EQ(no_state_prefetch_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()->AddDangerousUrl(
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);
}
// Ensures that server redirects to a malware page will cancel prerenders.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, ServerRedirect) {
GURL url = src_server()->GetURL("/prerender/prerender_page.html");
GetFakeSafeBrowsingDatabaseManager()->AddDangerousUrl(
url, safe_browsing::SB_THREAT_TYPE_URL_PHISHING);
PrefetchFromURL(src_server()->GetURL(
CreateServerRedirect("/prerender/prerender_page.html")),
FINAL_STATUS_SAFE_BROWSING, 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()->AddDangerousUrl(
url, safe_browsing::SB_THREAT_TYPE_URL_MALWARE);
constexpr char kPrefetchCanceledHistogram[] =
"SB2Test.RequestDestination.UnsafePrefetchCanceled";
base::RunLoop run_loop;
bool prefetch_canceled_histogram_added = false;
auto histogram_observer =
std::make_unique<base::StatisticsRecorder::ScopedHistogramSampleObserver>(
kPrefetchCanceledHistogram,
base::BindRepeating(
[](base::RepeatingClosure quit_closure, bool* called,
const char* histogram_name, uint64_t name_hash,
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);
}
// 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);
ASSERT_TRUE(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 BUILDFLAG(IS_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
absl::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.
std::u16string expected_title = u"SW READY";
content::TitleWatcher title_watcher(GetActiveWebContents(), expected_title);
ASSERT_TRUE(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);
}
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 prerenders are aborted when an incognito profile is closed.
// TODO(crbug.com/994068): The test is crashing on multiple platforms.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchIncognitoBrowserTest,
DISABLED_PrerenderIncognitoClosed) {
std::unique_ptr<TestPrerender> test_prerender =
PrefetchFromFile(kHungPrerenderPage, FINAL_STATUS_PROFILE_DESTROYED);
current_browser()->window()->Close();
test_prerender->WaitForStop();
}
// 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(),
chrome_browsing_data_remover::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);
GetNoStatePrefetchManager()->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);
GetNoStatePrefetchManager()->CancelAllPrerenders();
prerender->WaitForStop();
EXPECT_FALSE(prerender->contents());
}
// Checks shutdown code while a prerender is active.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrerenderQuickQuit) {
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(GetNoStatePrefetchManager());
GetNoStatePrefetchManager()->mutable_config().max_bytes = 100;
PrefetchFromURL(
src_server()->GetURL("/prerender/prerender_excessive_memory.html"),
FINAL_STATUS_MEMORY_LIMIT_EXCEEDED);
}
class NoStatePrefetchOmniboxBrowserTest : public NoStatePrefetchBrowserTest {
public:
LocationBar* GetLocationBar() {
return current_browser()->window()->GetLocationBar();
}
OmniboxView* GetOmniboxView() { return GetLocationBar()->GetOmniboxView(); }
predictors::AutocompleteActionPredictor* GetAutocompleteActionPredictor() {
Profile* profile = current_browser()->profile();
return predictors::AutocompleteActionPredictorFactory::GetForProfile(
profile);
}
std::unique_ptr<TestPrerender> ExpectPrerender(
FinalStatus expected_final_status) {
return no_state_prefetch_contents_factory()->ExpectNoStatePrefetchContents(
expected_final_status);
}
std::unique_ptr<TestPrerender> StartOmniboxPrerender(
const GURL& url,
FinalStatus expected_final_status) {
std::unique_ptr<TestPrerender> prerender =
ExpectPrerender(expected_final_status);
content::WebContents* web_contents = GetActiveWebContents();
GetAutocompleteActionPredictor()->StartPrerendering(url, *web_contents,
gfx::Size(50, 50));
prerender->WaitForStart();
return prerender;
}
};
// Checks that closing the omnibox popup cancels an omnibox prerender.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchOmniboxBrowserTest,
PrerenderOmniboxCancel) {
// Fake an omnibox prerender.
std::unique_ptr<TestPrerender> prerender = StartOmniboxPrerender(
embedded_test_server()->GetURL("/empty.html"), FINAL_STATUS_CANCELLED);
// Revert the location bar. This should cancel the prerender.
GetLocationBar()->Revert();
prerender->WaitForStop();
}
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, OpenTaskManager) {
const std::u16string any_tab = MatchTaskManagerTab("*");
const std::u16string original = MatchTaskManagerTab("Prefetch Loader");
const std::u16string prefetch_page = MatchTaskManagerTab("Prefetch Page");
// Show the task manager. This populates the model.
chrome::OpenTaskManager(current_browser());
ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, any_tab));
ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(0, prefetch_page));
// Prerender a page in addition to the original tab.
PrefetchFromFile(kPrefetchPage, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, original));
ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, any_tab));
// ui_test_utils::NavigateToURL(current_browser(),
// src_server()->GetURL(kPrefetchPage));
// Open a new tab to replace the one closed with all the RenderProcessHosts.
ui_test_utils::NavigateToURLWithDisposition(
current_browser(), src_server()->GetURL(kPrefetchPage),
WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
ASSERT_NO_FATAL_FAILURE(WaitForTaskManagerRows(1, prefetch_page));
}
// Renders a page that contains a prerender link to a page that contains an
// img with a source that requires http authentication. This should not
// prerender successfully.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest,
PrerenderHttpAuthentication) {
GURL url =
src_server()->GetURL("/prerender/prerender_http_auth_container.html");
std::unique_ptr<TestPrerender> prerender =
PrefetchFromURL(url, FINAL_STATUS_AUTH_NEEDED);
}
// Checks that the referrer is not set when prerendering and the source page is
// HTTPS.
IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, PrerenderNoSSLReferrer) {
// Use http:// url for the prerendered page main resource.
GURL url(
embedded_test_server()->GetURL("/prerender/prerender_no_referrer.html"));
// Use https:// for all resources.
UseHttpsSrcServer();
PrefetchFromURL(url, FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
ASSERT_TRUE(ui_test_utils::NavigateToURL(current_browser(), url));
EXPECT_TRUE(WaitForLoadStop(
current_browser()->tab_strip_model()->GetActiveWebContents()));
content::WebContents* web_contents =
current_browser()->tab_strip_model()->GetActiveWebContents();
const std::string referrer =
EvalJs(web_contents, "document.referrer").ExtractString();
EXPECT_TRUE(referrer.empty());
}
// Test class to verify speculation hints for non-private same origin no state
// prefetches.
class SpeculationNoStatePrefetchBrowserTest
: public NoStatePrefetchBrowserTest {
public:
void SetUp() override {
feature_list_.InitAndEnableFeature(
blink::features::kSpeculationRulesPrefetchProxy);
NoStatePrefetchBrowserTest::SetUp();
}
void InsertSpeculation(const GURL& prefetch_url,
FinalStatus expected_final_status,
bool should_navigate_away = false) {
std::string speculation_script = R"(
var script = document.createElement('script');
script.type = 'speculationrules';
script.text = `{)";
speculation_script.append(R"("prefetch_with_subresources": [{)");
speculation_script.append(R"("source": "list",
"urls": [)");
speculation_script.append("\"").append(prefetch_url.spec()).append("\"");
speculation_script.append(R"(]
}]
}`;
document.head.appendChild(script);)");
std::unique_ptr<TestPrerender> test_prerender =
no_state_prefetch_contents_factory()->ExpectNoStatePrefetchContents(
expected_final_status);
EXPECT_TRUE(ExecuteScript(GetActiveWebContents(), speculation_script));
if (should_navigate_away) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(
current_browser(), src_server()->GetURL("/defaultresponse?page")));
}
test_prerender->WaitForStop();
}
private:
base::test::ScopedFeatureList feature_list_;
};
IN_PROC_BROWSER_TEST_F(SpeculationNoStatePrefetchBrowserTest,
SpeculationPrefetch) {
UseHttpsSrcServer();
ASSERT_TRUE(ui_test_utils::NavigateToURL(
current_browser(), src_server()->GetURL("/defaultresponse?landing")));
InsertSpeculation(src_server()->GetURL(kPrefetchPage),
FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForRequestCount(src_server()->GetURL(kPrefetchPage), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 1);
}
IN_PROC_BROWSER_TEST_F(SpeculationNoStatePrefetchBrowserTest,
SpeculationDisallowsCrossOriginRedirect) {
UseHttpsSrcServer();
ASSERT_TRUE(ui_test_utils::NavigateToURL(
current_browser(), src_server()->GetURL("/defaultresponse?landing")));
InsertSpeculation(
src_server()->GetURL("/server-redirect-307?" +
src_server()->GetURL(kPrefetchPage).spec()),
FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
WaitForRequestCount(src_server()->GetURL(kPrefetchPage), 1);
WaitForRequestCount(src_server()->GetURL(kPrefetchScript), 1);
}
IN_PROC_BROWSER_TEST_F(SpeculationNoStatePrefetchBrowserTest,
SpeculationAllowsSameOriginRedirectBlocked) {
UseHttpsSrcServer();
ASSERT_TRUE(ui_test_utils::NavigateToURL(
current_browser(), src_server()->GetURL("/defaultresponse?landing")));
InsertSpeculation(src_server()->GetURL(
"/server-redirect-307?" +
embedded_test_server()->GetURL(kPrefetchPage).spec()),
FINAL_STATUS_UNSUPPORTED_SCHEME);
EXPECT_EQ(0u, GetRequestCount(embedded_test_server()->GetURL(kPrefetchPage)));
EXPECT_EQ(0u,
GetRequestCount(embedded_test_server()->GetURL(kPrefetchScript)));
}
IN_PROC_BROWSER_TEST_F(SpeculationNoStatePrefetchBrowserTest,
HungSpeculationTimedOutByNavigation) {
// The test assumes the previous page gets deleted after navigation. Disable
// back/forward cache to ensure that it doesn't get preserved in the cache.
content::DisableBackForwardCacheForTesting(
browser()->tab_strip_model()->GetActiveWebContents(),
content::BackForwardCache::TEST_ASSUMES_NO_CACHING);
UseHttpsSrcServer();
GetNoStatePrefetchManager()->mutable_config().abandon_time_to_live =
base::Milliseconds(500);
ASSERT_TRUE(ui_test_utils::NavigateToURL(
current_browser(), src_server()->GetURL("/defaultresponse?landing")));
InsertSpeculation(src_server()->GetURL("/hung"), FINAL_STATUS_TIMED_OUT,
/*should_navigate_away=*/true);
}
} // namespace prerender