blob: 2b22f8800ea6e0507624a76ca60eb3cec04082f5 [file] [log] [blame]
// Copyright 2021 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 "content/browser/preloading/prerender/prerender_metrics.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/metrics_hashes.h"
#include "content/browser/devtools/devtools_instrumentation.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/public/browser/prerender_trigger_type.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
namespace content {
namespace {
PrerenderCancelledInterface GetCancelledInterfaceType(
const std::string& interface_name) {
if (interface_name == "device.mojom.GamepadHapticsManager")
return PrerenderCancelledInterface::kGamepadHapticsManager;
else if (interface_name == "device.mojom.GamepadMonitor")
return PrerenderCancelledInterface::kGamepadMonitor;
else if (interface_name == "chrome.mojom.SyncEncryptionKeysExtension")
return PrerenderCancelledInterface::kSyncEncryptionKeysExtension;
return PrerenderCancelledInterface::kUnknown;
}
int32_t InterfaceNameHasher(const std::string& interface_name) {
return static_cast<int32_t>(base::HashMetricNameAs32Bits(interface_name));
}
std::string GenerateHistogramName(const std::string& histogram_base_name,
PrerenderTriggerType trigger_type,
const std::string& embedder_suffix) {
switch (trigger_type) {
case PrerenderTriggerType::kSpeculationRule:
DCHECK(embedder_suffix.empty());
return std::string(histogram_base_name) + ".SpeculationRule";
case PrerenderTriggerType::kEmbedder:
DCHECK(!embedder_suffix.empty());
return std::string(histogram_base_name) + ".Embedder_" + embedder_suffix;
}
NOTREACHED();
}
} // namespace
// Called by MojoBinderPolicyApplier. This function records the Mojo interface
// that causes MojoBinderPolicyApplier to cancel prerendering.
void RecordPrerenderCancelledInterface(
const std::string& interface_name,
PrerenderTriggerType trigger_type,
const std::string& embedder_histogram_suffix) {
const PrerenderCancelledInterface interface_type =
GetCancelledInterfaceType(interface_name);
base::UmaHistogramEnumeration(
GenerateHistogramName(
"Prerender.Experimental.PrerenderCancelledInterface", trigger_type,
embedder_histogram_suffix),
interface_type);
if (interface_type == PrerenderCancelledInterface::kUnknown) {
// These interfaces can be required by embedders, or not set to kCancel
// expclitly, e.g., channel-associated interfaces. Record these interfaces
// with the sparse histogram to ensure all of them are tracked.
base::UmaHistogramSparse(
GenerateHistogramName(
"Prerender.Experimental.PrerenderCancelledUnknownInterface",
trigger_type, embedder_histogram_suffix),
InterfaceNameHasher(interface_name));
}
}
void RecordPrerenderTriggered(ukm::SourceId ukm_id) {
ukm::builders::PrerenderPageLoad(ukm_id).SetTriggeredPrerender(true).Record(
ukm::UkmRecorder::Get());
}
void RecordPrerenderActivationTime(
base::TimeDelta delta,
PrerenderTriggerType trigger_type,
const std::string& embedder_histogram_suffix) {
base::UmaHistogramTimes(
GenerateHistogramName("Navigation.TimeToActivatePrerender", trigger_type,
embedder_histogram_suffix),
delta);
}
void RecordPrerenderHostFinalStatus(PrerenderHost::FinalStatus status,
const PrerenderAttributes& attributes,
ukm::SourceId prerendered_ukm_id) {
base::UmaHistogramEnumeration(
GenerateHistogramName("Prerender.Experimental.PrerenderHostFinalStatus",
attributes.trigger_type,
attributes.embedder_histogram_suffix),
status);
if (attributes.initiator_ukm_id != ukm::kInvalidSourceId) {
// `initiator_ukm_id` must be valid for the speculation rules.
DCHECK_EQ(attributes.trigger_type, PrerenderTriggerType::kSpeculationRule);
ukm::builders::PrerenderPageLoad(attributes.initiator_ukm_id)
.SetFinalStatus(static_cast<int>(status))
.Record(ukm::UkmRecorder::Get());
}
if (prerendered_ukm_id != ukm::kInvalidSourceId) {
// `prerendered_ukm_id` must be valid only when the prerendered page gets
// activated.
DCHECK_EQ(status, PrerenderHost::FinalStatus::kActivated);
ukm::builders::PrerenderPageLoad(prerendered_ukm_id)
.SetFinalStatus(static_cast<int>(status))
.Record(ukm::UkmRecorder::Get());
}
// The kActivated case is recorded in `PrerenderHost::Activate`, and the
// kMojoBinderPolicy case is recorded in
// RenderFrameHostImpl::CancelPrerenderingByMojoBinderPolicy for storing the
// interface detail. Browser initiated prerendering doesn't report
// cancellation reasons to the DevTools as it doesn't have the initiator frame
// associated with DevTools agents.
if (!attributes.IsBrowserInitiated() &&
status != PrerenderHost::FinalStatus::kActivated &&
status != PrerenderHost::FinalStatus::kMojoBinderPolicy) {
auto* ftn = FrameTreeNode::GloballyFindByID(
attributes.initiator_frame_tree_node_id);
DCHECK(ftn);
devtools_instrumentation::DidCancelPrerender(attributes.prerendering_url,
ftn, status, "");
}
}
} // namespace content