blob: be0448d298ba39d02ac82c10ee8e6c9df656f0f1 [file] [log] [blame]
// 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/prefetch_proxy/prefetch_proxy_subresource_manager.h"
#include "base/metrics/histogram_macros.h"
#include "chrome/browser/prefetch/prefetch_proxy/prefetch_proxy_prefetch_metrics_collector.h"
#include "chrome/browser/prefetch/prefetch_proxy/prefetched_mainframe_response_container.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
PrefetchProxySubresourceManager::PrefetchProxySubresourceManager(
const GURL& url,
std::unique_ptr<PrefetchedMainframeResponseContainer> mainframe_response)
: url_(url), mainframe_response_(std::move(mainframe_response)) {}
PrefetchProxySubresourceManager::~PrefetchProxySubresourceManager() {
if (nsp_handle_) {
nsp_handle_->SetObserver(nullptr);
nsp_handle_->OnCancel();
}
UMA_HISTOGRAM_COUNTS_100("PrefetchProxy.Prefetch.Subresources.Quantity",
successfully_loaded_subresources_.size());
}
void PrefetchProxySubresourceManager::ManageNoStatePrefetch(
std::unique_ptr<prerender::NoStatePrefetchHandle> handle,
base::OnceClosure on_nsp_done_callback) {
on_nsp_done_callback_ = std::move(on_nsp_done_callback);
nsp_handle_ = std::move(handle);
nsp_handle_->SetObserver(this);
}
std::unique_ptr<PrefetchedMainframeResponseContainer>
PrefetchProxySubresourceManager::TakeMainframeResponse() {
return std::move(mainframe_response_);
}
void PrefetchProxySubresourceManager::SetCreateIsolatedLoaderFactoryCallback(
CreateIsolatedLoaderFactoryRepeatingCallback callback) {
create_isolated_loader_factory_callback_ = std::move(callback);
}
void PrefetchProxySubresourceManager::NotifyPageNavigatedToAfterSRP() {
DCHECK(create_isolated_loader_factory_callback_);
// We're navigating so take the extra work off the CPU.
if (nsp_handle_) {
OnPrefetchStop(nsp_handle_.get());
}
was_navigated_to_after_srp_ = true;
}
void PrefetchProxySubresourceManager::OnPrefetchStop(
prerender::NoStatePrefetchHandle* handle) {
DCHECK_EQ(nsp_handle_.get(), handle);
if (on_nsp_done_callback_) {
std::move(on_nsp_done_callback_).Run();
}
// The handle must be canceled before it can be destroyed.
nsp_handle_->OnCancel();
nsp_handle_.reset();
}
bool PrefetchProxySubresourceManager::ShouldProxyForPrerenderNavigation(
int render_process_id,
content::ContentBrowserClient::URLLoaderFactoryType type) {
if (type != content::ContentBrowserClient::URLLoaderFactoryType::
kDocumentSubResource) {
return false;
}
if (!nsp_handle_) {
return false;
}
content::WebContents* web_contents =
nsp_handle_->contents()->no_state_prefetch_contents();
if (!web_contents) {
// This shouldn't happen, so abort the prerender just to be safe.
OnPrefetchStop(nsp_handle_.get());
NOTREACHED();
return false;
}
int prerender_process_id =
web_contents->GetMainFrame()->GetProcess()->GetID();
if (prerender_process_id != render_process_id) {
return false;
}
if (!create_isolated_loader_factory_callback_) {
// This also shouldn't happen, and would imply that there is a bug in the
// code where a prerender was triggered without having an isolated URL
// Loader Factory callback to use. Abort the prerender just to be safe.
OnPrefetchStop(nsp_handle_.get());
NOTREACHED();
return false;
}
return true;
}
bool PrefetchProxySubresourceManager::ShouldProxyForAfterSRPNavigation() const {
return was_navigated_to_after_srp_;
}
bool PrefetchProxySubresourceManager::MaybeProxyURLLoaderFactory(
content::RenderFrameHost* frame,
int render_process_id,
content::ContentBrowserClient::URLLoaderFactoryType type,
mojo::PendingReceiver<network::mojom::URLLoaderFactory>* factory_receiver) {
if (!ShouldProxyForPrerenderNavigation(render_process_id, type) &&
!ShouldProxyForAfterSRPNavigation()) {
return false;
}
auto proxied_receiver = std::move(*factory_receiver);
mojo::PendingRemote<network::mojom::URLLoaderFactory>
network_process_factory_remote;
*factory_receiver =
network_process_factory_remote.InitWithNewPipeAndPassReceiver();
mojo::PendingRemote<network::mojom::URLLoaderFactory> isolated_factory_remote;
create_isolated_loader_factory_callback_.Run(
isolated_factory_remote.InitWithNewPipeAndPassReceiver(),
frame->GetPendingIsolationInfoForSubresources());
auto proxy = std::make_unique<PrefetchProxyProxyingURLLoaderFactory>(
this, frame->GetFrameTreeNodeId(), std::move(proxied_receiver),
std::move(network_process_factory_remote),
std::move(isolated_factory_remote),
base::BindOnce(
&PrefetchProxySubresourceManager::RemoveProxiedURLLoaderFactory,
weak_factory_.GetWeakPtr()),
base::BindRepeating(
&PrefetchProxySubresourceManager::OnSubresourceLoadSuccessful,
weak_factory_.GetWeakPtr()));
if (ShouldProxyForAfterSRPNavigation()) {
proxy->NotifyPageNavigatedToAfterSRP(successfully_loaded_subresources_);
}
proxied_loader_factories_.emplace(std::move(proxy));
return true;
}
void PrefetchProxySubresourceManager::OnSubresourceLoadSuccessful(
const GURL& url) {
successfully_loaded_subresources_.emplace(url);
}
void PrefetchProxySubresourceManager::NotifyProbeFailed() {
successfully_loaded_subresources_.clear();
}
void PrefetchProxySubresourceManager::RemoveProxiedURLLoaderFactory(
PrefetchProxyProxyingURLLoaderFactory* factory) {
auto it = proxied_loader_factories_.find(factory);
DCHECK(it != proxied_loader_factories_.end());
proxied_loader_factories_.erase(it);
}
void PrefetchProxySubresourceManager::SetPrefetchMetricsCollector(
scoped_refptr<PrefetchProxyPrefetchMetricsCollector> collector) {
metrics_collector_ = collector;
}
void PrefetchProxySubresourceManager::OnResourceFetchComplete(
const GURL& url,
network::mojom::URLResponseHeadPtr head,
const network::URLLoaderCompletionStatus& status) {
if (!metrics_collector_)
return;
metrics_collector_->OnSubresourcePrefetched(
/*mainframe_url=*/url_,
/*subresource_url=*/url, std::move(head), status);
}
void PrefetchProxySubresourceManager::OnResourceNotEligible(
const GURL& url,
PrefetchProxyPrefetchStatus status) {
if (!metrics_collector_)
return;
metrics_collector_->OnSubresourceNotEligible(
/*mainframe_url=*/url_,
/*subresource_url=*/url, status);
}
void PrefetchProxySubresourceManager::OnResourceThrottled(const GURL& url) {
if (!metrics_collector_)
return;
metrics_collector_->OnSubresourceNotEligible(
/*mainframe_url=*/url_,
/*subresource_url=*/url,
PrefetchProxyPrefetchStatus::kSubresourceThrottled);
}
void PrefetchProxySubresourceManager::OnProxyUnavailableForResource(
const GURL& url) {
if (!metrics_collector_)
return;
metrics_collector_->OnSubresourceNotEligible(
/*mainframe_url=*/url_,
/*subresource_url=*/url,
PrefetchProxyPrefetchStatus::kPrefetchProxyNotAvailable);
}
void PrefetchProxySubresourceManager::OnResourceUsedFromCache(const GURL& url) {
if (!metrics_collector_)
return;
metrics_collector_->OnCachedSubresourceUsed(/*mainframe_url=*/url_,
/*subresource_url=*/url);
}