blob: ea1159af4009899f89e237ebebf805cc1099ada2 [file] [log] [blame]
// Copyright 2019 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/common/content_to_visible_time_reporter.h"
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/debug/dump_without_crashing.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/trace_event/trace_event.h"
#include "ui/gfx/presentation_feedback.h"
namespace content {
namespace {
bool IsOptionalValueTrue(const base::Optional<bool>& data) {
return data.has_value() && data.value();
}
// Used to generate unique "TabSwitching::Latency" event ids. Note: The address
// of ContentToVisibleTimeReporter can't be used as an id because a single
// ContentToVisibleTimeReporter can generate multiple overlapping events.
int g_num_trace_events_in_process = 0;
const char* GetHistogramSuffix(
bool has_saved_frames,
const RecordContentToVisibleTimeRequest& start_state) {
if (has_saved_frames)
return "WithSavedFrames";
if (IsOptionalValueTrue(start_state.destination_is_loaded)) {
if (IsOptionalValueTrue(start_state.destination_is_frozen)) {
return "NoSavedFrames_Loaded_Frozen";
} else {
return "NoSavedFrames_Loaded_NotFrozen";
}
} else {
return "NoSavedFrames_NotLoaded";
}
}
void ReportUnOccludedMetric(const base::TimeTicks requested_time,
const gfx::PresentationFeedback& feedback) {
const base::TimeDelta delta = feedback.timestamp - requested_time;
UMA_HISTOGRAM_TIMES("Aura.WebContentsWindowUnOccludedTime", delta);
}
void RecordBackForwardCacheRestoreMetric(
const base::TimeTicks requested_time,
const gfx::PresentationFeedback& feedback) {
const base::TimeDelta delta = feedback.timestamp - requested_time;
// Histogram to record the content to visible duration after restoring a page
// from back-forward cache. Here min, max bucket size are same as the
// "PageLoad.PaintTiming.NavigationToFirstContentfulPaint" metric.
base::UmaHistogramCustomTimes(
"BackForwardCache.Restore.NavigationToFirstPaint", delta,
base::TimeDelta::FromMilliseconds(10), base::TimeDelta::FromMinutes(10),
100);
}
} // namespace
RecordContentToVisibleTimeRequest::RecordContentToVisibleTimeRequest() =
default;
RecordContentToVisibleTimeRequest::~RecordContentToVisibleTimeRequest() =
default;
RecordContentToVisibleTimeRequest::RecordContentToVisibleTimeRequest(
const RecordContentToVisibleTimeRequest& other) = default;
RecordContentToVisibleTimeRequest::RecordContentToVisibleTimeRequest(
base::TimeTicks event_start_time,
base::Optional<bool> destination_is_loaded,
base::Optional<bool> destination_is_frozen,
bool show_reason_tab_switching,
bool show_reason_unoccluded,
bool show_reason_bfcache_restore)
: event_start_time(event_start_time),
destination_is_loaded(destination_is_loaded),
destination_is_frozen(destination_is_frozen),
show_reason_tab_switching(show_reason_tab_switching),
show_reason_unoccluded(show_reason_unoccluded),
show_reason_bfcache_restore(show_reason_bfcache_restore) {}
void RecordContentToVisibleTimeRequest::UpdateRequest(
const RecordContentToVisibleTimeRequest& other) {
event_start_time = std::min(event_start_time, other.event_start_time);
if (IsOptionalValueTrue(other.destination_is_loaded))
destination_is_loaded = other.destination_is_loaded;
if (IsOptionalValueTrue(other.destination_is_frozen))
destination_is_frozen = other.destination_is_frozen;
show_reason_tab_switching |= other.show_reason_tab_switching;
show_reason_unoccluded |= other.show_reason_unoccluded;
show_reason_bfcache_restore |= other.show_reason_bfcache_restore;
}
ContentToVisibleTimeReporter::ContentToVisibleTimeReporter() = default;
ContentToVisibleTimeReporter::~ContentToVisibleTimeReporter() = default;
base::OnceCallback<void(const gfx::PresentationFeedback&)>
ContentToVisibleTimeReporter::TabWasShown(
bool has_saved_frames,
const RecordContentToVisibleTimeRequest& start_state,
base::TimeTicks render_widget_visibility_request_timestamp) {
DCHECK(!start_state.event_start_time.is_null());
DCHECK(!render_widget_visibility_request_timestamp.is_null());
DCHECK(!tab_switch_start_state_);
DCHECK(render_widget_visibility_request_timestamp_.is_null());
has_saved_frames_ = has_saved_frames;
tab_switch_start_state_ = start_state;
render_widget_visibility_request_timestamp_ =
render_widget_visibility_request_timestamp;
// |tab_switch_start_state_| is only reset by RecordHistogramsAndTraceEvents
// once the metrics have been emitted.
return base::BindOnce(
&ContentToVisibleTimeReporter::RecordHistogramsAndTraceEvents,
weak_ptr_factory_.GetWeakPtr(), false /* is_incomplete */,
start_state.show_reason_tab_switching, start_state.show_reason_unoccluded,
start_state.show_reason_bfcache_restore);
}
void ContentToVisibleTimeReporter::TabWasHidden() {
if (tab_switch_start_state_) {
RecordHistogramsAndTraceEvents(true /* is_incomplete */,
true /* show_reason_tab_switching */,
false /* show_reason_unoccluded */,
false /* show_reason_bfcache_restore */,
gfx::PresentationFeedback::Failure());
weak_ptr_factory_.InvalidateWeakPtrs();
}
}
void ContentToVisibleTimeReporter::RecordHistogramsAndTraceEvents(
bool is_incomplete,
bool show_reason_tab_switching,
bool show_reason_unoccluded,
bool show_reason_bfcache_restore,
const gfx::PresentationFeedback& feedback) {
DCHECK(tab_switch_start_state_);
DCHECK(!render_widget_visibility_request_timestamp_.is_null());
// If the DCHECK fail, make sure RenderWidgetHostImpl::WasShown was triggered
// for recording the event.
DCHECK(show_reason_bfcache_restore || show_reason_unoccluded ||
show_reason_tab_switching);
if (show_reason_bfcache_restore) {
RecordBackForwardCacheRestoreMetric(
tab_switch_start_state_->event_start_time, feedback);
}
if (show_reason_unoccluded) {
ReportUnOccludedMetric(tab_switch_start_state_->event_start_time, feedback);
}
if (!show_reason_tab_switching)
return;
// Tab switching has occurred.
auto tab_switch_result = TabSwitchResult::kSuccess;
if (is_incomplete)
tab_switch_result = TabSwitchResult::kIncomplete;
else if (feedback.flags & gfx::PresentationFeedback::kFailure)
tab_switch_result = TabSwitchResult::kPresentationFailure;
const auto tab_switch_duration =
feedback.timestamp - tab_switch_start_state_->event_start_time;
// Record trace events.
TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP0(
"latency", "TabSwitching::Latency",
TRACE_ID_LOCAL(g_num_trace_events_in_process),
tab_switch_start_state_->event_start_time);
TRACE_EVENT_ASYNC_END_WITH_TIMESTAMP2(
"latency", "TabSwitching::Latency",
TRACE_ID_LOCAL(g_num_trace_events_in_process), feedback.timestamp,
"result", tab_switch_result, "latency",
tab_switch_duration.InMillisecondsF());
++g_num_trace_events_in_process;
// Each value recorded to a histogram with suffix .NoSavedFrames_Loaded_Frozen
// or .NoSavedFrames_Loaded_NotFrozen is also recorded to a histogram with
// suffix .NoSavedFrames_Loaded to facilitate assessing the impact of
// experiments that affect the number of frozen tab on tab switch time.
const bool is_no_saved_frames_loaded =
!has_saved_frames_ &&
IsOptionalValueTrue(
tab_switch_start_state_.value().destination_is_loaded);
// Record result histogram.
base::UmaHistogramEnumeration(
std::string("Browser.Tabs.TabSwitchResult.") +
GetHistogramSuffix(has_saved_frames_,
tab_switch_start_state_.value()),
tab_switch_result);
if (is_no_saved_frames_loaded) {
UMA_HISTOGRAM_ENUMERATION(
"Browser.Tabs.TabSwitchResult.NoSavedFrames_Loaded", tab_switch_result);
}
// Record latency histogram.
switch (tab_switch_result) {
case TabSwitchResult::kSuccess: {
base::UmaHistogramTimes(
std::string("Browser.Tabs.TotalSwitchDuration.") +
GetHistogramSuffix(has_saved_frames_,
tab_switch_start_state_.value()),
tab_switch_duration);
if (is_no_saved_frames_loaded) {
UMA_HISTOGRAM_TIMES(
"Browser.Tabs.TotalSwitchDuration.NoSavedFrames_Loaded",
tab_switch_duration);
}
break;
}
case TabSwitchResult::kIncomplete: {
base::UmaHistogramTimes(
std::string("Browser.Tabs.TotalIncompleteSwitchDuration.") +
GetHistogramSuffix(has_saved_frames_,
tab_switch_start_state_.value()),
tab_switch_duration);
if (is_no_saved_frames_loaded) {
UMA_HISTOGRAM_TIMES(
"Browser.Tabs.TotalIncompleteSwitchDuration.NoSavedFrames_Loaded",
tab_switch_duration);
}
break;
}
case TabSwitchResult::kPresentationFailure: {
break;
}
}
// Record legacy latency histogram.
UMA_HISTOGRAM_TIMES(
"MPArch.RWH_TabSwitchPaintDuration",
feedback.timestamp - render_widget_visibility_request_timestamp_);
// Reset tab switch information.
has_saved_frames_ = false;
tab_switch_start_state_.reset();
render_widget_visibility_request_timestamp_ = base::TimeTicks();
}
} // namespace content