Commit 8670b576 authored by Ryan Sturm's avatar Ryan Sturm Committed by Commit Bot

Adding interception logic for search prefetch

This functionality serves a prefetched response from the search prefetch
service when the search terms match from the prefetch vs the current
navigtation.

Bug: 1138643
Change-Id: Icf326fff7e29158d7c197bab8fdb39f6eb30539f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2487875
Commit-Queue: Ryan Sturm <ryansturm@chromium.org>
Reviewed-by: default avatarRobert Ogden <robertogden@chromium.org>
Cr-Commit-Position: refs/heads/master@{#819456}
parent 8c11a717
...@@ -1270,10 +1270,14 @@ static_library("browser") { ...@@ -1270,10 +1270,14 @@ static_library("browser") {
"prefetch/search_prefetch/field_trial_settings.h", "prefetch/search_prefetch/field_trial_settings.h",
"prefetch/search_prefetch/prefetched_response_container.cc", "prefetch/search_prefetch/prefetched_response_container.cc",
"prefetch/search_prefetch/prefetched_response_container.h", "prefetch/search_prefetch/prefetched_response_container.h",
"prefetch/search_prefetch/search_prefetch_from_string_url_loader.cc",
"prefetch/search_prefetch/search_prefetch_from_string_url_loader.h",
"prefetch/search_prefetch/search_prefetch_service.cc", "prefetch/search_prefetch/search_prefetch_service.cc",
"prefetch/search_prefetch/search_prefetch_service.h", "prefetch/search_prefetch/search_prefetch_service.h",
"prefetch/search_prefetch/search_prefetch_service_factory.cc", "prefetch/search_prefetch/search_prefetch_service_factory.cc",
"prefetch/search_prefetch/search_prefetch_service_factory.h", "prefetch/search_prefetch/search_prefetch_service_factory.h",
"prefetch/search_prefetch/search_prefetch_url_loader_interceptor.cc",
"prefetch/search_prefetch/search_prefetch_url_loader_interceptor.h",
"prefs/browser_prefs.cc", "prefs/browser_prefs.cc",
"prefs/browser_prefs.h", "prefs/browser_prefs.h",
"prefs/chrome_command_line_pref_store.cc", "prefs/chrome_command_line_pref_store.cc",
......
...@@ -91,6 +91,10 @@ ...@@ -91,6 +91,10 @@
#include "chrome/browser/platform_util.h" #include "chrome/browser/platform_util.h"
#include "chrome/browser/plugins/pdf_iframe_navigation_throttle.h" #include "chrome/browser/plugins/pdf_iframe_navigation_throttle.h"
#include "chrome/browser/plugins/plugin_utils.h" #include "chrome/browser/plugins/plugin_utils.h"
#include "chrome/browser/prefetch/search_prefetch/field_trial_settings.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/prefetch/search_prefetch/search_prefetch_url_loader_interceptor.h"
#include "chrome/browser/prerender/chrome_prerender_contents_delegate.h" #include "chrome/browser/prerender/chrome_prerender_contents_delegate.h"
#include "chrome/browser/prerender/isolated/isolated_prerender_features.h" #include "chrome/browser/prerender/isolated/isolated_prerender_features.h"
#include "chrome/browser/prerender/isolated/isolated_prerender_service.h" #include "chrome/browser/prerender/isolated/isolated_prerender_service.h"
...@@ -4770,6 +4774,11 @@ ChromeContentBrowserClient::WillCreateURLLoaderRequestInterceptors( ...@@ -4770,6 +4774,11 @@ ChromeContentBrowserClient::WillCreateURLLoaderRequestInterceptors(
frame_tree_node_id)); frame_tree_node_id));
} }
if (SearchPrefetchServiceIsEnabled()) {
interceptors.push_back(std::make_unique<SearchPrefetchURLLoaderInterceptor>(
frame_tree_node_id));
}
return interceptors; return interceptors;
} }
......
// 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/search_prefetch_from_string_url_loader.h"
#include "base/bind.h"
#include "base/check_op.h"
#include "base/memory/ptr_util.h"
#include "base/notreached.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "mojo/public/cpp/system/simple_watcher.h"
#include "net/base/io_buffer.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
SearchPrefetchFromStringURLLoader::SearchPrefetchFromStringURLLoader(
std::unique_ptr<PrefetchedResponseContainer> response,
const network::ResourceRequest& tentative_resource_request)
: head_(response->TakeHead()),
body_buffer_(
base::MakeRefCounted<net::StringIOBuffer>(response->TakeBody())),
bytes_of_raw_data_to_transfer_(body_buffer_->size()) {}
SearchPrefetchFromStringURLLoader::~SearchPrefetchFromStringURLLoader() =
default;
void SearchPrefetchFromStringURLLoader::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) {
NOTREACHED();
}
void SearchPrefetchFromStringURLLoader::SetPriority(
net::RequestPriority priority,
int32_t intra_priority_value) {
// Ignore: this class doesn't have a concept of priority.
}
void SearchPrefetchFromStringURLLoader::PauseReadingBodyFromNet() {
// Ignore: this class doesn't read from network.
}
void SearchPrefetchFromStringURLLoader::ResumeReadingBodyFromNet() {
// Ignore: this class doesn't read from network.
}
void SearchPrefetchFromStringURLLoader::TransferRawData() {
while (true) {
DCHECK_GE(bytes_of_raw_data_to_transfer_, write_position_);
uint32_t write_size =
static_cast<uint32_t>(bytes_of_raw_data_to_transfer_ - write_position_);
if (write_size == 0) {
Finish(net::OK);
return;
}
MojoResult result =
producer_handle_->WriteData(body_buffer_->data() + write_position_,
&write_size, MOJO_WRITE_DATA_FLAG_NONE);
if (result == MOJO_RESULT_SHOULD_WAIT) {
handle_watcher_->ArmOrNotify();
return;
}
if (result != MOJO_RESULT_OK) {
Finish(net::ERR_FAILED);
return;
}
// |write_position_| should only be updated when the mojo pipe has
// successfully been written to.
write_position_ += write_size;
}
}
SearchPrefetchFromStringURLLoader::RequestHandler
SearchPrefetchFromStringURLLoader::ServingResponseHandler() {
return base::BindOnce(&SearchPrefetchFromStringURLLoader::BindAndStart,
weak_ptr_factory_.GetWeakPtr());
}
void SearchPrefetchFromStringURLLoader::BindAndStart(
const network::ResourceRequest& request,
mojo::PendingReceiver<network::mojom::URLLoader> receiver,
mojo::PendingRemote<network::mojom::URLLoaderClient> client) {
DCHECK(!receiver_.is_bound());
receiver_.Bind(std::move(receiver));
receiver_.set_disconnect_handler(
base::BindOnce(&SearchPrefetchFromStringURLLoader::OnMojoDisconnect,
weak_ptr_factory_.GetWeakPtr()));
client_.Bind(std::move(client));
mojo::ScopedDataPipeProducerHandle producer_handle;
mojo::ScopedDataPipeConsumerHandle consumer_handle;
MojoResult rv =
mojo::CreateDataPipe(nullptr, &producer_handle, &consumer_handle);
if (rv != MOJO_RESULT_OK) {
Finish(net::ERR_FAILED);
return;
}
client_->OnReceiveResponse(std::move(head_));
client_->OnStartLoadingResponseBody(std::move(consumer_handle));
producer_handle_ = std::move(producer_handle);
handle_watcher_ = std::make_unique<mojo::SimpleWatcher>(
FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL,
base::SequencedTaskRunnerHandle::Get());
handle_watcher_->Watch(
producer_handle_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
MOJO_WATCH_CONDITION_SATISFIED,
base::BindRepeating(&SearchPrefetchFromStringURLLoader::OnHandleReady,
weak_ptr_factory_.GetWeakPtr()));
TransferRawData();
}
void SearchPrefetchFromStringURLLoader::OnHandleReady(
MojoResult result,
const mojo::HandleSignalsState& state) {
if (result != MOJO_RESULT_OK) {
Finish(net::ERR_FAILED);
return;
}
TransferRawData();
}
void SearchPrefetchFromStringURLLoader::Finish(int error) {
client_->OnComplete(network::URLLoaderCompletionStatus(error));
handle_watcher_.reset();
producer_handle_.reset();
client_.reset();
receiver_.reset();
weak_ptr_factory_.InvalidateWeakPtrs();
MaybeDeleteSelf();
}
void SearchPrefetchFromStringURLLoader::OnMojoDisconnect() {
receiver_.reset();
client_.reset();
MaybeDeleteSelf();
}
void SearchPrefetchFromStringURLLoader::MaybeDeleteSelf() {
if (!receiver_.is_bound() && !client_.is_bound()) {
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_SEARCH_PREFETCH_FROM_STRING_URL_LOADER_H_
#define CHROME_BROWSER_PREFETCH_SEARCH_PREFETCH_SEARCH_PREFETCH_FROM_STRING_URL_LOADER_H_
#include <cstdint>
#include <memory>
#include <string>
#include "base/memory/weak_ptr.h"
#include "chrome/browser/prefetch/search_prefetch/prefetched_response_container.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 "mojo/public/cpp/system/data_pipe.h"
#include "services/network/public/mojom/url_loader.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
namespace mojo {
class SimpleWatcher;
}
namespace net {
class StringIOBuffer;
}
class SearchPrefetchFromStringURLLoader : public network::mojom::URLLoader {
public:
using RequestHandler = base::OnceCallback<void(
const network::ResourceRequest& resource_request,
mojo::PendingReceiver<network::mojom::URLLoader> url_loader_receiver,
mojo::PendingRemote<network::mojom::URLLoaderClient> client)>;
SearchPrefetchFromStringURLLoader(
std::unique_ptr<PrefetchedResponseContainer> response,
const network::ResourceRequest& tentative_resource_request);
~SearchPrefetchFromStringURLLoader() override;
SearchPrefetchFromStringURLLoader(const SearchPrefetchFromStringURLLoader&) =
delete;
SearchPrefetchFromStringURLLoader& operator=(
const SearchPrefetchFromStringURLLoader&) = delete;
// Called when the response should be served to the user. Returns a handler.
RequestHandler ServingResponseHandler();
private:
// 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;
// Binds |this| to the mojo handlers and starts the network request using
// |request|. After this method is called, |this| manages its own lifetime.
void BindAndStart(
const network::ResourceRequest& request,
mojo::PendingReceiver<network::mojom::URLLoader> url_loader_receiver,
mojo::PendingRemote<network::mojom::URLLoaderClient> forwarding_client);
// Called when the mojo handle's state changes, either by being ready for more
// data or an error.
void OnHandleReady(MojoResult result, const mojo::HandleSignalsState& state);
// Finishes the request with the given net error.
void Finish(int error);
// Sends data on the mojo pipe.
void TransferRawData();
// Unbinds and deletes |this|.
void OnMojoDisconnect();
// Deletes |this| if it is not bound to the mojo pipes.
void MaybeDeleteSelf();
// The response that will be sent to mojo.
network::mojom::URLResponseHeadPtr head_;
scoped_refptr<net::StringIOBuffer> body_buffer_;
// Keeps track of the position of the data transfer.
int write_position_ = 0;
// The length of |body_buffer_|.
const int bytes_of_raw_data_to_transfer_ = 0;
// Mojo plumbing.
mojo::Receiver<network::mojom::URLLoader> receiver_{this};
mojo::Remote<network::mojom::URLLoaderClient> client_;
mojo::ScopedDataPipeProducerHandle producer_handle_;
std::unique_ptr<mojo::SimpleWatcher> handle_watcher_;
base::WeakPtrFactory<SearchPrefetchFromStringURLLoader> weak_ptr_factory_{
this};
};
#endif // CHROME_BROWSER_PREFETCH_SEARCH_PREFETCH_SEARCH_PREFETCH_FROM_STRING_URL_LOADER_H_
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "net/traffic_annotation/network_traffic_annotation.h" #include "net/traffic_annotation/network_traffic_annotation.h"
#include "services/network/public/cpp/resource_request.h" #include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h" #include "services/network/public/cpp/shared_url_loader_factory.h"
#include "url/origin.h"
SearchPrefetchService::PrefetchRequest::PrefetchRequest( SearchPrefetchService::PrefetchRequest::PrefetchRequest(
const GURL& prefetch_url) const GURL& prefetch_url)
...@@ -117,6 +118,11 @@ void SearchPrefetchService::PrefetchRequest::LoadDone( ...@@ -117,6 +118,11 @@ void SearchPrefetchService::PrefetchRequest::LoadDone(
simple_loader_.reset(); simple_loader_.reset();
} }
std::unique_ptr<PrefetchedResponseContainer>
SearchPrefetchService::PrefetchRequest::TakePrefetchResponse() {
return std::move(prefetch_response_container_);
}
SearchPrefetchService::SearchPrefetchService(Profile* profile) SearchPrefetchService::SearchPrefetchService(Profile* profile)
: profile_(profile) { : profile_(profile) {
DCHECK(!profile_->IsOffTheRecord()); DCHECK(!profile_->IsOffTheRecord());
...@@ -160,3 +166,50 @@ SearchPrefetchService::GetSearchPrefetchStatusForTesting( ...@@ -160,3 +166,50 @@ SearchPrefetchService::GetSearchPrefetchStatusForTesting(
return base::nullopt; return base::nullopt;
return prefetches_[search_terms]->current_status(); return prefetches_[search_terms]->current_status();
} }
std::unique_ptr<PrefetchedResponseContainer>
SearchPrefetchService::TakePrefetchResponse(const GURL& url) {
auto* template_url_service =
TemplateURLServiceFactory::GetForProfile(profile_);
if (!template_url_service)
return nullptr;
base::string16 search_terms;
template_url_service->GetDefaultSearchProvider()->ExtractSearchTermsFromURL(
url, template_url_service->search_terms_data(), &search_terms);
if (search_terms.length() == 0) {
return nullptr;
}
const auto& iter = prefetches_.find(search_terms);
if (iter == prefetches_.end()) {
return nullptr;
}
// Verify that the URL is the same origin as the prefetch URL. While other
// checks should address this by clearing prefetches on user changes to
// default search, it is paramount to never serve content from one origin to
// another.
if (url::Origin::Create(url) !=
url::Origin::Create(iter->second->prefetch_url())) {
return nullptr;
}
if (iter->second->current_status() !=
SearchPrefetchStatus::kSuccessfullyCompleted) {
return nullptr;
}
std::unique_ptr<PrefetchedResponseContainer> response =
iter->second->TakePrefetchResponse();
// TODO(ryansturm): For metrics reporting, the prefetch request data should be
// moved to the correct tab helper object, for now, the object can be deleted
// entirely. Alternatively, the object can remain here with a new timeout in
// a set of currently being served requests.
prefetches_.erase(iter);
return response;
}
...@@ -38,6 +38,10 @@ class SearchPrefetchService : public KeyedService { ...@@ -38,6 +38,10 @@ class SearchPrefetchService : public KeyedService {
// Returns whether the prefetch started or not. // Returns whether the prefetch started or not.
bool MaybePrefetchURL(const GURL& url); bool MaybePrefetchURL(const GURL& url);
// Takes the response from this object if |url| matches a prefetched URL.
std::unique_ptr<PrefetchedResponseContainer> TakePrefetchResponse(
const GURL& 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);
...@@ -58,6 +62,11 @@ class SearchPrefetchService : public KeyedService { ...@@ -58,6 +62,11 @@ class SearchPrefetchService : public KeyedService {
SearchPrefetchStatus current_status() const { return current_status_; } SearchPrefetchStatus current_status() const { return current_status_; }
const GURL& prefetch_url() const { return prefetch_url_; }
// Takes ownership of the prefetched data.
std::unique_ptr<PrefetchedResponseContainer> TakePrefetchResponse();
private: private:
// Called as a callback when the prefetch request is complete. Stores the // Called as a callback when the prefetch request is complete. Stores the
// response and other metadata in |prefetch_response_container_|. // response and other metadata in |prefetch_response_container_|.
......
...@@ -101,6 +101,16 @@ class SearchPrefetchBaseBrowserTest : public InProcessBrowserTest { ...@@ -101,6 +101,16 @@ class SearchPrefetchBaseBrowserTest : public InProcessBrowserTest {
} }
} }
content::WebContents* GetWebContents() const {
return browser()->tab_strip_model()->GetActiveWebContents();
}
std::string GetDocumentInnerHTML() const {
return content::EvalJs(GetWebContents(),
"document.documentElement.innerHTML")
.ExtractString();
}
private: private:
std::unique_ptr<net::test_server::HttpResponse> HandleSearchRequest( std::unique_ptr<net::test_server::HttpResponse> HandleSearchRequest(
const net::test_server::HttpRequest& request) { const net::test_server::HttpRequest& request) {
...@@ -122,7 +132,7 @@ class SearchPrefetchBaseBrowserTest : public InProcessBrowserTest { ...@@ -122,7 +132,7 @@ class SearchPrefetchBaseBrowserTest : public InProcessBrowserTest {
std::make_unique<net::test_server::BasicHttpResponse>(); std::make_unique<net::test_server::BasicHttpResponse>();
resp->set_code(is_prefetch ? net::HTTP_BAD_GATEWAY : net::HTTP_OK); resp->set_code(is_prefetch ? net::HTTP_BAD_GATEWAY : net::HTTP_OK);
resp->set_content_type("text/html"); resp->set_content_type("text/html");
resp->set_content("<html><body>Test</body></html>"); resp->set_content("<html><body></body></html>");
return resp; return resp;
} }
...@@ -130,7 +140,10 @@ class SearchPrefetchBaseBrowserTest : public InProcessBrowserTest { ...@@ -130,7 +140,10 @@ class SearchPrefetchBaseBrowserTest : public InProcessBrowserTest {
std::make_unique<net::test_server::BasicHttpResponse>(); std::make_unique<net::test_server::BasicHttpResponse>();
resp->set_code(net::HTTP_OK); resp->set_code(net::HTTP_OK);
resp->set_content_type("text/html"); resp->set_content_type("text/html");
resp->set_content("<html><body>" + request.relative_url + "</body></html>"); std::string content = "<html><body> ";
content.append(is_prefetch ? "prefetch" : "regular");
content.append(" </body></html>");
resp->set_content(content);
return resp; return resp;
} }
...@@ -330,3 +343,83 @@ IN_PROC_BROWSER_TEST_F(SearchPrefetchServiceEnabledBrowserTest, BadURL) { ...@@ -330,3 +343,83 @@ IN_PROC_BROWSER_TEST_F(SearchPrefetchServiceEnabledBrowserTest, BadURL) {
EXPECT_FALSE(search_prefetch_service->MaybePrefetchURL(prefetch_url)); EXPECT_FALSE(search_prefetch_service->MaybePrefetchURL(prefetch_url));
} }
IN_PROC_BROWSER_TEST_F(SearchPrefetchServiceEnabledBrowserTest,
BasicPrefetchServed) {
auto* search_prefetch_service =
SearchPrefetchServiceFactory::GetForProfile(browser()->profile());
EXPECT_NE(nullptr, search_prefetch_service);
std::string search_terms = "prefetch_content";
GURL prefetch_url = GetSearchServerQueryURL(search_terms);
EXPECT_TRUE(search_prefetch_service->MaybePrefetchURL(prefetch_url));
auto prefetch_status =
search_prefetch_service->GetSearchPrefetchStatusForTesting(
base::ASCIIToUTF16(search_terms));
WaitUntilStatusChanges(base::ASCIIToUTF16(search_terms));
prefetch_status = search_prefetch_service->GetSearchPrefetchStatusForTesting(
base::ASCIIToUTF16(search_terms));
ASSERT_TRUE(prefetch_status.has_value());
EXPECT_EQ(SearchPrefetchStatus::kSuccessfullyCompleted,
prefetch_status.value());
ui_test_utils::NavigateToURL(browser(), prefetch_url);
auto inner_html = GetDocumentInnerHTML();
EXPECT_FALSE(base::Contains(inner_html, "regular"));
EXPECT_TRUE(base::Contains(inner_html, "prefetch"));
}
IN_PROC_BROWSER_TEST_F(SearchPrefetchServiceEnabledBrowserTest,
RegularSearchQueryWhenNoPrefetch) {
auto* search_prefetch_service =
SearchPrefetchServiceFactory::GetForProfile(browser()->profile());
EXPECT_NE(nullptr, search_prefetch_service);
std::string search_terms = "prefetch_content";
GURL search_url = GetSearchServerQueryURL(search_terms);
ui_test_utils::NavigateToURL(browser(), search_url);
auto inner_html = GetDocumentInnerHTML();
EXPECT_TRUE(base::Contains(inner_html, "regular"));
EXPECT_FALSE(base::Contains(inner_html, "prefetch"));
}
IN_PROC_BROWSER_TEST_F(SearchPrefetchServiceEnabledBrowserTest,
NonMatchingPrefetchURL) {
auto* search_prefetch_service =
SearchPrefetchServiceFactory::GetForProfile(browser()->profile());
EXPECT_NE(nullptr, search_prefetch_service);
std::string search_terms = "prefetch_content";
std::string search_terms_other = "other";
GURL prefetch_url = GetSearchServerQueryURL(search_terms);
EXPECT_TRUE(search_prefetch_service->MaybePrefetchURL(prefetch_url));
auto prefetch_status =
search_prefetch_service->GetSearchPrefetchStatusForTesting(
base::ASCIIToUTF16(search_terms));
WaitUntilStatusChanges(base::ASCIIToUTF16(search_terms));
prefetch_status = search_prefetch_service->GetSearchPrefetchStatusForTesting(
base::ASCIIToUTF16(search_terms));
ASSERT_TRUE(prefetch_status.has_value());
EXPECT_EQ(SearchPrefetchStatus::kSuccessfullyCompleted,
prefetch_status.value());
ui_test_utils::NavigateToURL(browser(),
GetSearchServerQueryURL(search_terms_other));
auto inner_html = GetDocumentInnerHTML();
EXPECT_TRUE(base::Contains(inner_html, "regular"));
EXPECT_FALSE(base::Contains(inner_html, "prefetch"));
}
// 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/search_prefetch_url_loader_interceptor.h"
#include <memory>
#include "base/bind.h"
#include "base/callback.h"
#include "base/feature_list.h"
#include "base/metrics/histogram_macros.h"
#include "chrome/browser/prefetch/search_prefetch/prefetched_response_container.h"
#include "chrome/browser/prefetch/search_prefetch/search_prefetch_from_string_url_loader.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 "components/prerender/browser/prerender_manager.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
namespace {
Profile* ProfileFromFrameTreeNodeID(int frame_tree_node_id) {
content::WebContents* web_contents =
content::WebContents::FromFrameTreeNodeId(frame_tree_node_id);
if (!web_contents)
return nullptr;
return Profile::FromBrowserContext(web_contents->GetBrowserContext());
}
} // namespace
SearchPrefetchURLLoaderInterceptor::SearchPrefetchURLLoaderInterceptor(
int frame_tree_node_id)
: frame_tree_node_id_(frame_tree_node_id) {}
SearchPrefetchURLLoaderInterceptor::~SearchPrefetchURLLoaderInterceptor() =
default;
void SearchPrefetchURLLoaderInterceptor::MaybeCreateLoader(
const network::ResourceRequest& tentative_resource_request,
content::BrowserContext* browser_context,
content::URLLoaderRequestInterceptor::LoaderCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!loader_callback_);
loader_callback_ = std::move(callback);
url_ = tentative_resource_request.url;
std::unique_ptr<PrefetchedResponseContainer> prefetch =
GetPrefetchedResponse(url_);
if (!prefetch) {
DoNotInterceptPrefetchedNavigation();
return;
}
InterceptPrefetchedNavigation(tentative_resource_request,
std::move(prefetch));
}
void SearchPrefetchURLLoaderInterceptor::InterceptPrefetchedNavigation(
const network::ResourceRequest& tentative_resource_request,
std::unique_ptr<PrefetchedResponseContainer> prefetch) {
std::unique_ptr<SearchPrefetchFromStringURLLoader> url_loader =
std::make_unique<SearchPrefetchFromStringURLLoader>(
std::move(prefetch), tentative_resource_request);
std::move(loader_callback_).Run(url_loader->ServingResponseHandler());
// url_loader manages its own lifetime once bound to the mojo pipes.
url_loader.release();
}
void SearchPrefetchURLLoaderInterceptor::DoNotInterceptPrefetchedNavigation() {
std::move(loader_callback_).Run({});
}
std::unique_ptr<PrefetchedResponseContainer>
SearchPrefetchURLLoaderInterceptor::GetPrefetchedResponse(const GURL& url) {
auto* profile = ProfileFromFrameTreeNodeID(frame_tree_node_id_);
if (!profile)
return nullptr;
SearchPrefetchService* service =
SearchPrefetchServiceFactory::GetForProfile(profile);
if (!service)
return nullptr;
return service->TakePrefetchResponse(url);
}
// 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_SEARCH_PREFETCH_URL_LOADER_INTERCEPTOR_H_
#define CHROME_BROWSER_PREFETCH_SEARCH_PREFETCH_SEARCH_PREFETCH_URL_LOADER_INTERCEPTOR_H_
#include <memory>
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "content/public/browser/url_loader_request_interceptor.h"
#include "services/network/public/cpp/resource_request.h"
#include "url/gurl.h"
namespace content {
class BrowserContext;
} // namespace content
class PrefetchedResponseContainer;
// Intercepts search navigations that were previously prefetched.
class SearchPrefetchURLLoaderInterceptor
: public content::URLLoaderRequestInterceptor {
public:
explicit SearchPrefetchURLLoaderInterceptor(int frame_tree_node_id);
~SearchPrefetchURLLoaderInterceptor() override;
SearchPrefetchURLLoaderInterceptor(
const SearchPrefetchURLLoaderInterceptor&) = delete;
SearchPrefetchURLLoaderInterceptor& operator=(
const SearchPrefetchURLLoaderInterceptor&) = delete;
// content::URLLaoderRequestInterceptor:
void MaybeCreateLoader(
const network::ResourceRequest& tentative_resource_request,
content::BrowserContext* browser_context,
content::URLLoaderRequestInterceptor::LoaderCallback callback) override;
protected:
// Virtual for testing
virtual std::unique_ptr<PrefetchedResponseContainer> GetPrefetchedResponse(
const GURL& url);
private:
void InterceptPrefetchedNavigation(
const network::ResourceRequest& tentative_resource_request,
std::unique_ptr<PrefetchedResponseContainer>);
void DoNotInterceptPrefetchedNavigation();
bool MaybeInterceptNavigation(
const network::ResourceRequest& tentative_resource_request);
// Used to get the current WebContents/Profile.
const int frame_tree_node_id_;
// The url that |MaybeCreateLoader| is called with.
GURL url_;
// Set in |MaybeCreateLoader| and used in |On[DoNot]InterceptRequest|.
content::URLLoaderRequestInterceptor::LoaderCallback loader_callback_;
SEQUENCE_CHECKER(sequence_checker_);
};
#endif // CHROME_BROWSER_PREFETCH_SEARCH_PREFETCH_SEARCH_PREFETCH_URL_LOADER_INTERCEPTOR_H_
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