Commit a1ac016e authored by Ryan Sturm's avatar Ryan Sturm Committed by Chromium LUCI CQ

Allowing Search Prefetch responses to be served from cache on BF

BF and restore navigations are allowed to use cache even when the
cache-control headers would otherwise require revalidation. When this
occurs on a previously served Search Prefetch URL, Chrome should attempt
to serve the prefetch response from cache, and then fallback to the
navigation URL on the network.

Bug: 1162121
Change-Id: I5ec8c82fe772e57acce812987efed2baa3301ed2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2605414Reviewed-by: default avatarTarun Bansal <tbansal@chromium.org>
Commit-Queue: Ryan Sturm <ryansturm@chromium.org>
Cr-Commit-Position: refs/heads/master@{#840219}
parent bbc4035b
...@@ -1365,6 +1365,8 @@ static_library("browser") { ...@@ -1365,6 +1365,8 @@ static_library("browser") {
"prefetch/prefetch_proxy/prefetch_proxy_url_loader_interceptor.h", "prefetch/prefetch_proxy/prefetch_proxy_url_loader_interceptor.h",
"prefetch/prefetch_proxy/prefetched_mainframe_response_container.cc", "prefetch/prefetch_proxy/prefetched_mainframe_response_container.cc",
"prefetch/prefetch_proxy/prefetched_mainframe_response_container.h", "prefetch/prefetch_proxy/prefetched_mainframe_response_container.h",
"prefetch/search_prefetch/back_forward_search_prefetch_url_loader.cc",
"prefetch/search_prefetch/back_forward_search_prefetch_url_loader.h",
"prefetch/search_prefetch/base_search_prefetch_request.cc", "prefetch/search_prefetch/base_search_prefetch_request.cc",
"prefetch/search_prefetch/base_search_prefetch_request.h", "prefetch/search_prefetch/base_search_prefetch_request.h",
"prefetch/search_prefetch/field_trial_settings.cc", "prefetch/search_prefetch/field_trial_settings.cc",
......
// Copyright 2020 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 "chrome/browser/prefetch/search_prefetch/back_forward_search_prefetch_url_loader.h"
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "chrome/browser/prefetch/search_prefetch/search_prefetch_service.h"
#include "chrome/browser/prefetch/search_prefetch/search_prefetch_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/storage_partition.h"
#include "net/base/load_flags.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
#include "net/http/http_util.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/constants.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "url/gurl.h"
BackForwardSearchPrefetchURLLoader::BackForwardSearchPrefetchURLLoader(
Profile* profile,
const net::NetworkTrafficAnnotationTag& network_traffic_annotation,
const GURL& prefetch_url)
: profile_(profile),
network_traffic_annotation_(network_traffic_annotation),
prefetch_url_(prefetch_url) {}
BackForwardSearchPrefetchURLLoader::~BackForwardSearchPrefetchURLLoader() =
default;
SearchPrefetchURLLoader::RequestHandler
BackForwardSearchPrefetchURLLoader::ServingResponseHandler(
std::unique_ptr<SearchPrefetchURLLoader> loader) {
return base::BindOnce(
&BackForwardSearchPrefetchURLLoader::SetUpForwardingClient,
weak_factory_.GetWeakPtr(), std::move(loader));
}
void BackForwardSearchPrefetchURLLoader::SetUpForwardingClient(
std::unique_ptr<SearchPrefetchURLLoader> loader,
const network::ResourceRequest& resource_request,
mojo::PendingReceiver<network::mojom::URLLoader> receiver,
mojo::PendingRemote<network::mojom::URLLoaderClient> forwarding_client) {
resource_request_ =
std::make_unique<network::ResourceRequest>(resource_request);
network::ResourceRequest prefetch_request = *resource_request_;
prefetch_request.load_flags |= net::LOAD_ONLY_FROM_CACHE;
prefetch_request.url = prefetch_url_;
auto url_loader_factory =
content::BrowserContext::GetDefaultStoragePartition(profile_)
->GetURLLoaderFactoryForBrowserProcess();
// Create a network service URL loader with passed in params.
url_loader_factory->CreateLoaderAndStart(
network_url_loader_.BindNewPipeAndPassReceiver(), 0, 0,
network::mojom::kURLLoadOptionNone, prefetch_request,
url_loader_receiver_.BindNewPipeAndPassRemote(
base::ThreadTaskRunnerHandle::Get()),
net::MutableNetworkTrafficAnnotationTag(network_traffic_annotation_));
url_loader_receiver_.set_disconnect_handler(base::BindOnce(
&BackForwardSearchPrefetchURLLoader::MojoDisconnectForPrefetch,
base::Unretained(this)));
// Bind to the content/ navigation code.
DCHECK(!receiver_.is_bound());
// At this point, we are bound to the mojo receiver, so we can release
// |loader|, which points to |this|.
receiver_.Bind(std::move(receiver));
loader.release();
receiver_.set_disconnect_handler(base::BindOnce(
&BackForwardSearchPrefetchURLLoader::MojoDisconnectWithNoFallback,
weak_factory_.GetWeakPtr()));
forwarding_client_.Bind(std::move(forwarding_client));
}
void BackForwardSearchPrefetchURLLoader::RestartDirect() {
network_url_loader_.reset();
url_loader_receiver_.reset();
can_fallback_ = false;
SearchPrefetchService* service =
SearchPrefetchServiceFactory::GetForProfile(profile_);
if (service)
service->ClearCacheEntry(resource_request_->url);
auto url_loader_factory =
content::BrowserContext::GetDefaultStoragePartition(profile_)
->GetURLLoaderFactoryForBrowserProcess();
// Create a network service URL loader with passed in params.
url_loader_factory->CreateLoaderAndStart(
network_url_loader_.BindNewPipeAndPassReceiver(), 0, 0,
network::mojom::kURLLoadOptionNone, *resource_request_,
url_loader_receiver_.BindNewPipeAndPassRemote(
base::ThreadTaskRunnerHandle::Get()),
net::MutableNetworkTrafficAnnotationTag(network_traffic_annotation_));
url_loader_receiver_.set_disconnect_handler(base::BindOnce(
&BackForwardSearchPrefetchURLLoader::MojoDisconnectWithNoFallback,
base::Unretained(this)));
if (paused_) {
network_url_loader_->PauseReadingBodyFromNet();
}
}
void BackForwardSearchPrefetchURLLoader::OnReceiveResponse(
network::mojom::URLResponseHeadPtr head) {
DCHECK(forwarding_client_);
if (can_fallback_) {
if (!head->headers) {
RestartDirect();
return;
}
// Any 200 response can be served.
if (head->headers->response_code() < net::HTTP_OK ||
head->headers->response_code() >= net::HTTP_MULTIPLE_CHOICES) {
RestartDirect();
return;
}
url_loader_receiver_.set_disconnect_handler(base::BindOnce(
&BackForwardSearchPrefetchURLLoader::MojoDisconnectWithNoFallback,
weak_factory_.GetWeakPtr()));
SearchPrefetchService* service =
SearchPrefetchServiceFactory::GetForProfile(profile_);
if (service)
service->UpdateServeTime(resource_request_->url);
}
can_fallback_ = false;
forwarding_client_->OnReceiveResponse(std::move(head));
}
void BackForwardSearchPrefetchURLLoader::OnReceiveRedirect(
const net::RedirectInfo& redirect_info,
network::mojom::URLResponseHeadPtr head) {
DCHECK(forwarding_client_);
if (can_fallback_) {
RestartDirect();
return;
}
forwarding_client_->OnReceiveRedirect(redirect_info, std::move(head));
}
void BackForwardSearchPrefetchURLLoader::OnUploadProgress(
int64_t current_position,
int64_t total_size,
OnUploadProgressCallback callback) {
// We only handle GETs.
NOTREACHED();
}
void BackForwardSearchPrefetchURLLoader::OnReceiveCachedMetadata(
mojo_base::BigBuffer data) {
// Do nothing. This is not supported for navigation loader.
}
void BackForwardSearchPrefetchURLLoader::OnTransferSizeUpdated(
int32_t transfer_size_diff) {
DCHECK(forwarding_client_);
forwarding_client_->OnTransferSizeUpdated(transfer_size_diff);
}
void BackForwardSearchPrefetchURLLoader::OnStartLoadingResponseBody(
mojo::ScopedDataPipeConsumerHandle body) {
DCHECK(forwarding_client_);
forwarding_client_->OnStartLoadingResponseBody(std::move(body));
return;
}
void BackForwardSearchPrefetchURLLoader::OnComplete(
const network::URLLoaderCompletionStatus& status) {
if (status.error_code != net::OK && can_fallback_) {
RestartDirect();
return;
}
DCHECK(forwarding_client_);
can_fallback_ = false;
forwarding_client_->OnComplete(status);
network_url_loader_.reset();
}
void BackForwardSearchPrefetchURLLoader::FollowRedirect(
const std::vector<std::string>& removed_headers,
const net::HttpRequestHeaders& modified_headers,
const net::HttpRequestHeaders& modified_cors_exempt_headers,
const base::Optional<GURL>& new_url) {
// This should never be called for a non-network service URLLoader.
NOTREACHED();
}
void BackForwardSearchPrefetchURLLoader::SetPriority(
net::RequestPriority priority,
int32_t intra_priority_value) {
// Pass through.
if (network_url_loader_)
network_url_loader_->SetPriority(priority, intra_priority_value);
resource_request_->priority = priority;
}
void BackForwardSearchPrefetchURLLoader::PauseReadingBodyFromNet() {
// Pass through.
if (network_url_loader_)
network_url_loader_->PauseReadingBodyFromNet();
paused_ = true;
}
void BackForwardSearchPrefetchURLLoader::ResumeReadingBodyFromNet() {
// Pass through.
if (network_url_loader_)
network_url_loader_->ResumeReadingBodyFromNet();
paused_ = false;
}
void BackForwardSearchPrefetchURLLoader::MojoDisconnectForPrefetch() {
if (can_fallback_)
RestartDirect();
}
void BackForwardSearchPrefetchURLLoader::MojoDisconnectWithNoFallback() {
delete this;
}
// Copyright 2020 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.
#ifndef CHROME_BROWSER_PREFETCH_SEARCH_PREFETCH_BACK_FORWARD_SEARCH_PREFETCH_URL_LOADER_H_
#define CHROME_BROWSER_PREFETCH_SEARCH_PREFETCH_BACK_FORWARD_SEARCH_PREFETCH_URL_LOADER_H_
#include <vector>
#include "base/callback.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "chrome/browser/prefetch/search_prefetch/search_prefetch_url_loader.h"
#include "chrome/browser/prefetch/search_prefetch/streaming_search_prefetch_request.h"
#include "content/public/browser/url_loader_request_interceptor.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/network/public/mojom/url_loader.mojom-forward.h"
#include "services/network/public/mojom/url_response_head.mojom-forward.h"
// This class tries to fetch a prefetch response from cache, and if one is not
// available, it fetches the non-prefetch URL directly. This case is only
// triggered when cache doesn't need to be revalidated (i.e., back/forward).
class BackForwardSearchPrefetchURLLoader
: public network::mojom::URLLoader,
public network::mojom::URLLoaderClient,
public SearchPrefetchURLLoader {
public:
// Creates and stores state needed to do the cache lookup.
BackForwardSearchPrefetchURLLoader(
Profile* profile,
const net::NetworkTrafficAnnotationTag& network_traffic_annotation,
const GURL& prefetch_url);
~BackForwardSearchPrefetchURLLoader() override;
private:
// SearchPrefetchURLLoader:
SearchPrefetchURLLoader::RequestHandler ServingResponseHandler(
std::unique_ptr<SearchPrefetchURLLoader> loader) override;
// network::mojom::URLLoader:
void FollowRedirect(
const std::vector<std::string>& removed_headers,
const net::HttpRequestHeaders& modified_headers,
const net::HttpRequestHeaders& modified_cors_exempt_headers,
const base::Optional<GURL>& new_url) override;
void SetPriority(net::RequestPriority priority,
int32_t intra_priority_value) override;
void PauseReadingBodyFromNet() override;
void ResumeReadingBodyFromNet() override;
// network::mojom::URLLoaderClient
void OnReceiveResponse(network::mojom::URLResponseHeadPtr head) override;
void OnReceiveRedirect(const net::RedirectInfo& redirect_info,
network::mojom::URLResponseHeadPtr head) override;
void OnUploadProgress(int64_t current_position,
int64_t total_size,
OnUploadProgressCallback callback) override;
void OnReceiveCachedMetadata(mojo_base::BigBuffer data) override;
void OnTransferSizeUpdated(int32_t transfer_size_diff) override;
void OnStartLoadingResponseBody(
mojo::ScopedDataPipeConsumerHandle body) override;
void OnComplete(const network::URLLoaderCompletionStatus& status) override;
// Restarts the request to go directly to |resource_request_|.
void RestartDirect();
// The disconnect handler that is used for the fetch of the cached prefetch
// response. This handler is not used once a fallback is started or serving is
// started.
void MojoDisconnectForPrefetch();
// This handler is used for forwarding client errors and errors after a
// fallback can not occur.
void MojoDisconnectWithNoFallback();
// Sets up mojo forwarding to the navigation path. Resumes
// |network_url_loader_| calls. Serves the start of the response to the
// navigation path. After this method is called, |this| manages its own
// lifetime; |loader| points to |this| and can be released once the mojo
// connection is set up.
void SetUpForwardingClient(
std::unique_ptr<SearchPrefetchURLLoader> loader,
const network::ResourceRequest&,
mojo::PendingReceiver<network::mojom::URLLoader> receiver,
mojo::PendingRemote<network::mojom::URLLoaderClient> forwarding_client);
// The network URLLoader that fetches the prefetch URL and its receiver.
mojo::Remote<network::mojom::URLLoader> network_url_loader_;
mojo::Receiver<network::mojom::URLLoaderClient> url_loader_receiver_{this};
// The request that is being prefetched.
std::unique_ptr<network::ResourceRequest> resource_request_;
// Whether we are serving from |bdoy_content_|.
bool can_fallback_ = true;
// If the owner paused network activity, we need to propagate that if a
// fallback occurs.
bool paused_ = false;
Profile* profile_;
net::NetworkTrafficAnnotationTag network_traffic_annotation_;
// The URL for the prefetch response stored in cache.
GURL prefetch_url_;
// Forwarding client receiver.
mojo::Receiver<network::mojom::URLLoader> receiver_{this};
mojo::Remote<network::mojom::URLLoaderClient> forwarding_client_;
base::WeakPtrFactory<BackForwardSearchPrefetchURLLoader> weak_factory_{this};
};
#endif // CHROME_BROWSER_PREFETCH_SEARCH_PREFETCH_BACK_FORWARD_SEARCH_PREFETCH_URL_LOADER_H_
...@@ -94,9 +94,10 @@ BaseSearchPrefetchRequest::BaseSearchPrefetchRequest( ...@@ -94,9 +94,10 @@ BaseSearchPrefetchRequest::BaseSearchPrefetchRequest(
BaseSearchPrefetchRequest::~BaseSearchPrefetchRequest() = default; BaseSearchPrefetchRequest::~BaseSearchPrefetchRequest() = default;
bool BaseSearchPrefetchRequest::StartPrefetchRequest(Profile* profile) { // static
net::NetworkTrafficAnnotationTag network_traffic_annotation = net::NetworkTrafficAnnotationTag
net::DefineNetworkTrafficAnnotation("search_prefetch_service", R"( BaseSearchPrefetchRequest::NetworkAnnotationForPrefetch() {
return net::DefineNetworkTrafficAnnotation("search_prefetch_service", R"(
semantics { semantics {
sender: "Search Prefetch Service" sender: "Search Prefetch Service"
description: description:
...@@ -129,6 +130,11 @@ bool BaseSearchPrefetchRequest::StartPrefetchRequest(Profile* profile) { ...@@ -129,6 +130,11 @@ bool BaseSearchPrefetchRequest::StartPrefetchRequest(Profile* profile) {
} }
} }
})"); })");
}
bool BaseSearchPrefetchRequest::StartPrefetchRequest(Profile* profile) {
net::NetworkTrafficAnnotationTag network_traffic_annotation =
NetworkAnnotationForPrefetch();
url::Origin prefetch_origin = url::Origin::Create(prefetch_url_); url::Origin prefetch_origin = url::Origin::Create(prefetch_url_);
...@@ -137,7 +143,6 @@ bool BaseSearchPrefetchRequest::StartPrefetchRequest(Profile* profile) { ...@@ -137,7 +143,6 @@ bool BaseSearchPrefetchRequest::StartPrefetchRequest(Profile* profile) {
// navigation speeding and relatively high likelihood of being served to a // navigation speeding and relatively high likelihood of being served to a
// navigation, the request is relatively high priority. // navigation, the request is relatively high priority.
resource_request->priority = net::MEDIUM; resource_request->priority = net::MEDIUM;
resource_request->load_flags |= net::LOAD_PREFETCH;
resource_request->url = prefetch_url_; resource_request->url = prefetch_url_;
// Search prefetch URL Loaders should check |report_raw_headers| on the // Search prefetch URL Loaders should check |report_raw_headers| on the
// intercepted request to clear out the raw headers when |report_raw_headers| // intercepted request to clear out the raw headers when |report_raw_headers|
......
...@@ -60,6 +60,9 @@ class BaseSearchPrefetchRequest { ...@@ -60,6 +60,9 @@ class BaseSearchPrefetchRequest {
BaseSearchPrefetchRequest& operator=(const BaseSearchPrefetchRequest&) = BaseSearchPrefetchRequest& operator=(const BaseSearchPrefetchRequest&) =
delete; delete;
// The NTA for any search prefetch request.
static net::NetworkTrafficAnnotationTag NetworkAnnotationForPrefetch();
// Starts the network request to prefetch |prefetch_url_|. Sets various fields // Starts the network request to prefetch |prefetch_url_|. Sets various fields
// on a resource request and calls |StartPrefetchRequestInternal()|. Returns // on a resource request and calls |StartPrefetchRequestInternal()|. Returns
// |false| if the request is not started (i.e., it would be deferred by // |false| if the request is not started (i.e., it would be deferred by
...@@ -81,6 +84,9 @@ class BaseSearchPrefetchRequest { ...@@ -81,6 +84,9 @@ class BaseSearchPrefetchRequest {
// Update the status when the relevant search item is clicked in omnibox. // Update the status when the relevant search item is clicked in omnibox.
void MarkPrefetchAsClicked(); void MarkPrefetchAsClicked();
// The URL for the prefetch request.
const GURL& prefetch_url() { return prefetch_url_; }
// Whether the prefetch should be served based on |headers|. // Whether the prefetch should be served based on |headers|.
bool CanServePrefetchRequest( bool CanServePrefetchRequest(
const scoped_refptr<net::HttpResponseHeaders> headers); const scoped_refptr<net::HttpResponseHeaders> headers);
......
...@@ -73,3 +73,8 @@ bool StreamSearchPrefetchResponses() { ...@@ -73,3 +73,8 @@ bool StreamSearchPrefetchResponses() {
return base::GetFieldTrialParamByFeatureAsBool( return base::GetFieldTrialParamByFeatureAsBool(
kSearchPrefetchServicePrefetching, "stream_responses", true); kSearchPrefetchServicePrefetching, "stream_responses", true);
} }
size_t SearchPrefetchMaxCacheEntries() {
return base::GetFieldTrialParamByFeatureAsInt(
kSearchPrefetchServicePrefetching, "cache_size", 10);
}
...@@ -43,4 +43,8 @@ bool SearchPrefetchShouldCancelUneededInflightRequests(); ...@@ -43,4 +43,8 @@ bool SearchPrefetchShouldCancelUneededInflightRequests();
// requests. // requests.
bool StreamSearchPrefetchResponses(); bool StreamSearchPrefetchResponses();
// The max number of stored cached prefetch responses. This is stored as a list
// of navigation URLs to prefetch URLs.
size_t SearchPrefetchMaxCacheEntries();
#endif // CHROME_BROWSER_PREFETCH_SEARCH_PREFETCH_FIELD_TRIAL_SETTINGS_H_ #endif // CHROME_BROWSER_PREFETCH_SEARCH_PREFETCH_FIELD_TRIAL_SETTINGS_H_
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/net/prediction_options.h" #include "chrome/browser/net/prediction_options.h"
#include "chrome/browser/prefetch/search_prefetch/back_forward_search_prefetch_url_loader.h"
#include "chrome/browser/prefetch/search_prefetch/field_trial_settings.h" #include "chrome/browser/prefetch/search_prefetch/field_trial_settings.h"
#include "chrome/browser/prefetch/search_prefetch/full_body_search_prefetch_request.h" #include "chrome/browser/prefetch/search_prefetch/full_body_search_prefetch_request.h"
#include "chrome/browser/prefetch/search_prefetch/search_prefetch_url_loader.h" #include "chrome/browser/prefetch/search_prefetch/search_prefetch_url_loader.h"
...@@ -227,7 +228,8 @@ SearchPrefetchService::GetSearchPrefetchStatusForTesting( ...@@ -227,7 +228,8 @@ SearchPrefetchService::GetSearchPrefetchStatusForTesting(
} }
std::unique_ptr<SearchPrefetchURLLoader> std::unique_ptr<SearchPrefetchURLLoader>
SearchPrefetchService::TakePrefetchResponse(const GURL& url) { SearchPrefetchService::TakePrefetchResponseFromMemoryCache(
const GURL& navigation_url) {
SearchPrefetchServingReasonRecorder recorder; SearchPrefetchServingReasonRecorder recorder;
auto* template_url_service = auto* template_url_service =
...@@ -248,15 +250,16 @@ SearchPrefetchService::TakePrefetchResponse(const GURL& url) { ...@@ -248,15 +250,16 @@ SearchPrefetchService::TakePrefetchResponse(const GURL& url) {
auto* content_settings = auto* content_settings =
HostContentSettingsMapFactory::GetForProfile(profile_); HostContentSettingsMapFactory::GetForProfile(profile_);
if (!content_settings || if (!content_settings ||
content_settings->GetContentSetting( content_settings->GetContentSetting(navigation_url, navigation_url,
url, url, ContentSettingsType::JAVASCRIPT) == CONTENT_SETTING_BLOCK) { ContentSettingsType::JAVASCRIPT) ==
CONTENT_SETTING_BLOCK) {
recorder.reason_ = SearchPrefetchServingReason::kJavascriptDisabled; recorder.reason_ = SearchPrefetchServingReason::kJavascriptDisabled;
return nullptr; return nullptr;
} }
base::string16 search_terms; base::string16 search_terms;
template_url_service->GetDefaultSearchProvider()->ExtractSearchTermsFromURL( template_url_service->GetDefaultSearchProvider()->ExtractSearchTermsFromURL(
url, template_url_service->search_terms_data(), &search_terms); navigation_url, template_url_service->search_terms_data(), &search_terms);
if (search_terms.length() == 0) { if (search_terms.length() == 0) {
recorder.reason_ = SearchPrefetchServingReason::kNotDefaultSearchWithTerms; recorder.reason_ = SearchPrefetchServingReason::kNotDefaultSearchWithTerms;
...@@ -274,7 +277,7 @@ SearchPrefetchService::TakePrefetchResponse(const GURL& url) { ...@@ -274,7 +277,7 @@ SearchPrefetchService::TakePrefetchResponse(const GURL& url) {
// checks should address this by clearing prefetches on user changes to // checks should address this by clearing prefetches on user changes to
// default search, it is paramount to never serve content from one origin to // default search, it is paramount to never serve content from one origin to
// another. // another.
if (url::Origin::Create(url) != if (url::Origin::Create(navigation_url) !=
url::Origin::Create(iter->second->prefetch_url())) { url::Origin::Create(iter->second->prefetch_url())) {
recorder.reason_ = recorder.reason_ =
SearchPrefetchServingReason::kPrefetchWasForDifferentOrigin; SearchPrefetchServingReason::kPrefetchWasForDifferentOrigin;
...@@ -302,14 +305,29 @@ SearchPrefetchService::TakePrefetchResponse(const GURL& url) { ...@@ -302,14 +305,29 @@ SearchPrefetchService::TakePrefetchResponse(const GURL& url) {
std::unique_ptr<SearchPrefetchURLLoader> response = std::unique_ptr<SearchPrefetchURLLoader> response =
iter->second->TakeSearchPrefetchURLLoader(); iter->second->TakeSearchPrefetchURLLoader();
AddCacheEntry(navigation_url, iter->second->prefetch_url());
DeletePrefetch(search_terms); DeletePrefetch(search_terms);
return response; return response;
} }
std::unique_ptr<SearchPrefetchURLLoader>
SearchPrefetchService::TakePrefetchResponseFromDiskCache(
const GURL& navigation_url) {
if (prefetch_cache_.find(navigation_url) == prefetch_cache_.end()) {
return nullptr;
}
return std::make_unique<BackForwardSearchPrefetchURLLoader>(
profile_, BaseSearchPrefetchRequest::NetworkAnnotationForPrefetch(),
prefetch_cache_[navigation_url].first);
}
void SearchPrefetchService::ClearPrefetches() { void SearchPrefetchService::ClearPrefetches() {
prefetches_.clear(); prefetches_.clear();
prefetch_expiry_timers_.clear(); prefetch_expiry_timers_.clear();
prefetch_cache_.clear();
} }
void SearchPrefetchService::DeletePrefetch(base::string16 search_terms) { void SearchPrefetchService::DeletePrefetch(base::string16 search_terms) {
...@@ -418,3 +436,36 @@ void SearchPrefetchService::OnTemplateURLServiceChanged() { ...@@ -418,3 +436,36 @@ void SearchPrefetchService::OnTemplateURLServiceChanged() {
template_url_service_data_ = template_url_service_data; template_url_service_data_ = template_url_service_data;
ClearPrefetches(); ClearPrefetches();
} }
void SearchPrefetchService::ClearCacheEntry(const GURL& navigation_url) {
prefetch_cache_.erase(navigation_url);
}
void SearchPrefetchService::UpdateServeTime(const GURL& navigation_url) {
if (prefetch_cache_.find(navigation_url) == prefetch_cache_.end())
return;
prefetch_cache_[navigation_url].second = base::Time::Now();
}
void SearchPrefetchService::AddCacheEntry(const GURL& navigation_url,
const GURL& prefetch_url) {
// TODO(ryansturm): Add prefs support to handle session restore.
// https://crbug.com/1162121.
prefetch_cache_.emplace(navigation_url,
std::make_pair(prefetch_url, base::Time::Now()));
if (prefetch_cache_.size() <= SearchPrefetchMaxCacheEntries()) {
return;
}
GURL url_to_remove;
base::Time earliest_time = base::Time::Now();
for (const auto& entry : prefetch_cache_) {
base::Time last_used_time = entry.second.second;
if (last_used_time < earliest_time) {
earliest_time = last_used_time;
url_to_remove = entry.first;
}
}
ClearCacheEntry(url_to_remove);
}
...@@ -97,15 +97,30 @@ class SearchPrefetchService : public KeyedService, ...@@ -97,15 +97,30 @@ class SearchPrefetchService : public KeyedService,
// Clear all prefetches from the service. // Clear all prefetches from the service.
void ClearPrefetches(); void ClearPrefetches();
// Clear the disk cache entry for |url|.
void ClearCacheEntry(const GURL& navigation_url);
// Update the last serving time of |url|, so it's eviction priority is
// lowered.
void UpdateServeTime(const GURL& navigation_url);
// Takes the response from this object if |url| matches a prefetched URL. // Takes the response from this object if |url| matches a prefetched URL.
std::unique_ptr<SearchPrefetchURLLoader> TakePrefetchResponse( std::unique_ptr<SearchPrefetchURLLoader> TakePrefetchResponseFromMemoryCache(
const GURL& url); const GURL& navigation_url);
// Creates a cache loader to serve a cache only response with fallback to
// network fetch.
std::unique_ptr<SearchPrefetchURLLoader> TakePrefetchResponseFromDiskCache(
const GURL& navigation_url);
// Reports the status of a prefetch for a given search term. // Reports the status of a prefetch for a given search term.
base::Optional<SearchPrefetchStatus> GetSearchPrefetchStatusForTesting( base::Optional<SearchPrefetchStatus> GetSearchPrefetchStatusForTesting(
base::string16 search_terms); base::string16 search_terms);
private: private:
// Records a cache entry for a navigation that is being served.
void AddCacheEntry(const GURL& navigation_url, const GURL& prefetch_url);
// Removes the prefetch and prefetch timers associated with |search_terms|. // Removes the prefetch and prefetch timers associated with |search_terms|.
void DeletePrefetch(base::string16 search_terms); void DeletePrefetch(base::string16 search_terms);
...@@ -141,6 +156,11 @@ class SearchPrefetchService : public KeyedService, ...@@ -141,6 +156,11 @@ class SearchPrefetchService : public KeyedService,
observer_{this}; observer_{this};
Profile* profile_; Profile* profile_;
// A map of previously handled URLs that allows certain navigations to be
// served from cache. The value is the prefetch URL in cache and the latest
// serving time of the response.
std::map<GURL, std::pair<GURL, base::Time>> prefetch_cache_;
}; };
#endif // CHROME_BROWSER_PREFETCH_SEARCH_PREFETCH_SEARCH_PREFETCH_SERVICE_H_ #endif // CHROME_BROWSER_PREFETCH_SEARCH_PREFETCH_SEARCH_PREFETCH_SERVICE_H_
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_frame_host.h"
#include "content/public/browser/storage_partition.h" #include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "net/base/load_flags.h"
SearchPrefetchURLLoaderInterceptor::SearchPrefetchURLLoaderInterceptor( SearchPrefetchURLLoaderInterceptor::SearchPrefetchURLLoaderInterceptor(
int frame_tree_node_id) int frame_tree_node_id)
...@@ -56,7 +57,15 @@ SearchPrefetchURLLoaderInterceptor::MaybeCreateLoaderForRequest( ...@@ -56,7 +57,15 @@ SearchPrefetchURLLoaderInterceptor::MaybeCreateLoaderForRequest(
if (!service) if (!service)
return nullptr; return nullptr;
return service->TakePrefetchResponse(tentative_resource_request.url); auto loader = service->TakePrefetchResponseFromMemoryCache(
tentative_resource_request.url);
if (loader)
return loader;
if (tentative_resource_request.load_flags & net::LOAD_SKIP_CACHE_VALIDATION) {
return service->TakePrefetchResponseFromDiskCache(
tentative_resource_request.url);
}
return nullptr;
} }
void SearchPrefetchURLLoaderInterceptor::MaybeCreateLoader( void SearchPrefetchURLLoaderInterceptor::MaybeCreateLoader(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment