blob: d86b60c0e9d23b14a881159fbb606180a26a44d3 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/preloading/search_preload/search_preload_pipeline.h"
#include "chrome/browser/preloading/chrome_preloading.h"
#include "chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.h"
#include "chrome/browser/preloading/prerender/prerender_utils.h"
#include "chrome/browser/preloading/search_preload/search_preload_features.h"
#include "chrome/browser/preloading/search_preload/search_preload_service.h"
#include "content/public/browser/preloading_data.h"
#include "content/public/browser/preloading_trigger_type.h"
#include "content/public/browser/web_contents.h"
#include "net/http/http_no_vary_search_data.h"
#include "third_party/blink/public/mojom/loader/referrer.mojom.h"
SearchPreloadPipeline::SearchPreloadPipeline(GURL canonical_url)
: pipeline_info_(content::PreloadPipelineInfo::Create(
/*planned_max_preloading_type=*/content::PreloadingType::kPrerender)),
canonical_url_(std::move(canonical_url)) {}
SearchPreloadPipeline::~SearchPreloadPipeline() = default;
void SearchPreloadPipeline::UpdateConfidence(content::WebContents& web_contents,
int confidence) {
// Add new prediction when stronger signal arrived.
if (confidence <= confidence_) {
return;
}
confidence_ = confidence;
auto* preloading_data =
content::PreloadingData::GetOrCreateForWebContents(&web_contents);
// Safety: The ownership of this callback will be passed to
// `PreloadingDataImpl`, which has lifetime bounded by `web_contents`. So,
// it's safe to bind `content::BrowserContext*`.
content::PreloadingURLMatchCallback same_url_matcher =
base::BindRepeating(&IsSearchDestinationMatch, canonical_url_,
web_contents.GetBrowserContext());
preloading_data->AddPreloadingPrediction(
chrome_preloading_predictor::kDefaultSearchEngine, confidence,
std::move(same_url_matcher),
web_contents.GetPrimaryMainFrame()->GetPageUkmSourceId());
}
SearchPreloadSignalResult SearchPreloadPipeline::StartPrefetch(
content::WebContents& web_contents,
base::WeakPtr<SearchPreloadService> search_preload_service,
const GURL& prefetch_url,
content::PreloadingPredictor predictor,
const std::optional<net::HttpNoVarySearchData>& no_vary_search_hint,
bool is_navigation_likely) {
// Don't trigger prefetch if already triggered and is alive.
//
// TODO(crbug.com/394213503): Reconsider the behavior when prefetch is already
// triggered but not alive. Currently, the main reason that a triggered
// prefetch fails for DSE (embedder trigger, no TTL) is the failure of the
// load of the prefetch. (There should be no other timeouts nor expiration.)
// In general, retriggering may be useful.
if (prefetch_handle_) {
return SearchPreloadSignalResult::kNotTriggeredAlreadyTriggered;
}
auto* preloading_data =
content::PreloadingData::GetOrCreateForWebContents(&web_contents);
// Safety: The ownership of this callback will be passed to
// `PreloadingDataImpl`, which has lifetime bounded by `web_contents`. So,
// it's safe to bind `content::BrowserContext*`.
content::PreloadingURLMatchCallback same_url_matcher =
base::BindRepeating(&IsSearchDestinationMatch, canonical_url_,
web_contents.GetBrowserContext());
content::PreloadingAttempt* attempt = preloading_data->AddPreloadingAttempt(
predictor, content::PreloadingType::kPrefetch,
std::move(same_url_matcher),
/*triggering_primary_page_source_id=*/
web_contents.GetPrimaryMainFrame()->GetPageUkmSourceId());
// TODO(crbug.com/379140429): Create `preloading_utils` and move common
// preloading histograms suffixes to it.
prefetch_handle_ = web_contents.StartPrefetch(
prefetch_url,
/*use_prefetch_proxy=*/false,
prerender_utils::kDefaultSearchEngineMetricSuffix,
blink::mojom::Referrer(),
/*referring_origin=*/std::nullopt, no_vary_search_hint,
/*priority=*/std::nullopt, pipeline_info_, attempt->GetWeakPtr(),
/*holdback_status_override=*/
content::PreloadingHoldbackStatus::kUnspecified,
/*ttl=*/features::kDsePreload2PrefetchTtl.Get());
CHECK(prefetch_handle_);
prefetch_handle_->SetOnPrefetchHeadReceivedCallback(base::BindRepeating(
&SearchPreloadService::OnPrefetchHeadReceived, search_preload_service));
if (!is_navigation_likely) {
prefetch_handle_->SetOnPrefetchCompletedOrFailedCallback(
base::BindRepeating(
&SearchPreloadService::OnSuggestPrefetchCompletedOrFailed,
search_preload_service));
}
return SearchPreloadSignalResult::kPrefetchTriggered;
}
SearchPreloadSignalResult SearchPreloadPipeline::StartPrerender(
content::WebContents& web_contents,
const GURL& prerender_url,
content::PreloadingPredictor predictor) {
// Don't trigger prerender if already triggered.
if (prerender_handle_) {
return SearchPreloadSignalResult::kNotTriggeredAlreadyTriggered;
}
// Assume that prefetch is alive.
if (!IsPrefetchAlive()) {
return SearchPreloadSignalResult::kNotTriggeredPrefetchNotAlive;
}
auto* preloading_data =
content::PreloadingData::GetOrCreateForWebContents(&web_contents);
// Safety: The ownership of this callback will be passed to
// `PreloadingDataImpl`, which has lifetime bounded by `web_contents`. So,
// it's safe to bind `content::BrowserContext*`.
content::PreloadingURLMatchCallback same_url_matcher =
base::BindRepeating(&IsSearchDestinationMatch, canonical_url_,
web_contents.GetBrowserContext());
content::PreloadingAttempt* attempt = preloading_data->AddPreloadingAttempt(
predictor, content::PreloadingType::kPrerender,
std::move(same_url_matcher),
/*triggering_primary_page_source_id=*/
web_contents.GetPrimaryMainFrame()->GetPageUkmSourceId());
// Safety: The ownership of this callback will be passed to
// `PrerenderAttributes`, which has lifetime bounded by `web_contents`. So,
// it's safe to bind `content::BrowserContext*`.
base::RepeatingCallback<bool(const GURL&,
const std::optional<content::UrlMatchType>&)>
url_match_predicate =
base::BindRepeating(&IsSearchDestinationMatchWithWebUrlMatchResult,
canonical_url_, web_contents.GetBrowserContext());
prerender_handle_ = web_contents.StartPrerendering(
prerender_url, content::PreloadingTriggerType::kEmbedder,
prerender_utils::kDefaultSearchEngineMetricSuffix,
/*additional_headers=*/net::HttpRequestHeaders(),
/*no_vary_search_hint=*/std::nullopt,
ui::PageTransitionFromInt(ui::PAGE_TRANSITION_GENERATED |
ui::PAGE_TRANSITION_FROM_ADDRESS_BAR),
/*should_warm_up_compositor=*/true,
/*should_prepare_paint_tree=*/true,
content::PreloadingHoldbackStatus::kUnspecified, pipeline_info_, attempt,
std::move(url_match_predicate),
/*prerender_navigation_handle_callback=*/{},
/*allow_reuse=*/false);
return SearchPreloadSignalResult::kPrerenderTriggered;
}
void SearchPreloadPipeline::CancelPrerender() {
prerender_handle_.reset();
}
bool SearchPreloadPipeline::IsPrefetchAlive() const {
return prefetch_handle_ && prefetch_handle_->IsAlive();
}
bool SearchPreloadPipeline::IsPrerenderValid() const {
return prerender_handle_ && prerender_handle_->IsValid();
}