1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/history/domain_diversity_reporter.h"
6
7 #include "base/metrics/histogram_macros.h"
8 #include "base/threading/thread_task_runner_handle.h"
9 #include "components/pref_registry/pref_registry_syncable.h"
10 #include "components/prefs/pref_service.h"
11 #include "content/public/browser/browser_task_traits.h"
12 #include "content/public/browser/browser_thread.h"
13
14 namespace {
15 // The interval between two successive domain metrics reports.
16 constexpr base::TimeDelta kDomainDiversityReportingInterval =
17 base::TimeDelta::FromDays(1);
18
19 // Pref name for the persistent timestamp of the last report. This pref is
20 // per local profile but not synced.
21 constexpr char kDomainDiversityReportingTimestamp[] =
22 "domain_diversity.last_reporting_timestamp";
23 } // namespace
24
DomainDiversityReporter(history::HistoryService * history_service,PrefService * prefs,base::Clock * clock)25 DomainDiversityReporter::DomainDiversityReporter(
26 history::HistoryService* history_service,
27 PrefService* prefs,
28 base::Clock* clock)
29 : history_service_(history_service),
30 prefs_(prefs),
31 clock_(clock),
32 history_service_observer_(this) {
33 DCHECK_NE(prefs_, nullptr);
34 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
35
36 content::GetUIThreadTaskRunner({base::TaskPriority::BEST_EFFORT})
37 ->PostTask(
38 FROM_HERE,
39 base::BindOnce(&DomainDiversityReporter::MaybeComputeDomainMetrics,
40 weak_ptr_factory_.GetWeakPtr()));
41 }
42
43 DomainDiversityReporter::~DomainDiversityReporter() = default;
44
45 // static
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)46 void DomainDiversityReporter::RegisterProfilePrefs(
47 user_prefs::PrefRegistrySyncable* registry) {
48 registry->RegisterTimePref(kDomainDiversityReportingTimestamp, base::Time());
49 }
50
MaybeComputeDomainMetrics()51 void DomainDiversityReporter::MaybeComputeDomainMetrics() {
52 if (history_service_->BackendLoaded()) {
53 // HistoryService is ready; proceed to start the domain metrics
54 // computation task.
55 ComputeDomainMetrics();
56 }
57 // Observe history service and start reporting as soon as
58 // the former is ready.
59 DCHECK(!history_service_observer_.IsObserving(history_service_));
60 history_service_observer_.Add(history_service_);
61 }
62
ComputeDomainMetrics()63 void DomainDiversityReporter::ComputeDomainMetrics() {
64 base::Time time_last_report_triggered =
65 prefs_->GetTime(kDomainDiversityReportingTimestamp);
66 base::Time time_current_report_triggered = clock_->Now();
67
68 if (time_last_report_triggered < time_current_report_triggered) {
69 // The lower boundary of all times is set at Unix epoch, since
70 // LocalMidnight() may fail on times represented by a very small value
71 // (e.g. Windows epoch).
72 if (time_last_report_triggered < base::Time::UnixEpoch())
73 time_last_report_triggered = base::Time::UnixEpoch();
74
75 if (time_current_report_triggered < base::Time::UnixEpoch())
76 time_current_report_triggered = base::Time::UnixEpoch();
77
78 // Will only report up to 7 days x 3 results.
79 int number_of_days_to_report = 7;
80
81 // If the last report time is too far back in the past, simply use the
82 // highest possible value for |number_of_days_to_report| and skip its
83 // computation. This avoids calling LocalMidnight() on some very old
84 // timestamp that may cause unexpected behaviors on certain
85 // platforms/timezones (see https://crbug.com/1048145).
86 // The beginning and the end of a 7-day period may differ by at most
87 // 24 * 8 + 1(DST offset) hours; round up to FromDays(9) here.
88 if (time_current_report_triggered - time_last_report_triggered <
89 base::TimeDelta::FromDays(number_of_days_to_report + 2)) {
90 // Compute the number of days that needs to be reported for based on
91 // the last report time and current time.
92 base::TimeDelta report_time_range =
93 time_current_report_triggered.LocalMidnight() -
94 time_last_report_triggered.LocalMidnight();
95
96 // Due to daylight saving time, |report_time_range| may not be a multiple
97 // of 24 hours. A small time offset is therefore added to
98 // |report_time_range| so that the resulting time range is guaranteed to
99 // be at least the correct number of days times 24. The number of days to
100 // report is capped at 7 days.
101 number_of_days_to_report = std::min(
102 (report_time_range + base::TimeDelta::FromHours(4)).InDaysFloored(),
103 number_of_days_to_report);
104 }
105
106 if (number_of_days_to_report >= 1) {
107 history_service_->GetDomainDiversity(
108 /*report_time=*/time_current_report_triggered,
109 /*number_of_days_to_report=*/number_of_days_to_report,
110 /*metric_type_bitmask=*/history::kEnableLast1DayMetric |
111 history::kEnableLast7DayMetric | history::kEnableLast28DayMetric,
112 base::BindOnce(&DomainDiversityReporter::ReportDomainMetrics,
113 weak_ptr_factory_.GetWeakPtr(),
114 time_current_report_triggered),
115 &cancelable_task_tracker_);
116 }
117 }
118
119 // The next reporting task is scheduled to run 24 hours later.
120 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
121 FROM_HERE,
122 base::BindOnce(&DomainDiversityReporter::ComputeDomainMetrics,
123 weak_ptr_factory_.GetWeakPtr()),
124 kDomainDiversityReportingInterval);
125 }
126
ReportDomainMetrics(base::Time time_current_report_triggered,history::DomainDiversityResults result)127 void DomainDiversityReporter::ReportDomainMetrics(
128 base::Time time_current_report_triggered,
129 history::DomainDiversityResults result) {
130 // An empty DomainDiversityResults indicates that |db_| is null in
131 // HistoryBackend.
132 if (result.empty())
133 return;
134
135 for (auto& result_one_day : result) {
136 UMA_HISTOGRAM_COUNTS_1000("History.DomainCount1Day",
137 result_one_day.one_day_metric.value().count);
138 UMA_HISTOGRAM_COUNTS_1000("History.DomainCount7Day",
139 result_one_day.seven_day_metric.value().count);
140 UMA_HISTOGRAM_COUNTS_1000(
141 "History.DomainCount28Day",
142 result_one_day.twenty_eight_day_metric.value().count);
143 }
144
145 prefs_->SetTime(kDomainDiversityReportingTimestamp,
146 time_current_report_triggered);
147 }
148
OnHistoryServiceLoaded(history::HistoryService * history_service)149 void DomainDiversityReporter::OnHistoryServiceLoaded(
150 history::HistoryService* history_service) {
151 DCHECK_EQ(history_service, history_service_);
152 ComputeDomainMetrics();
153 }
154
HistoryServiceBeingDeleted(history::HistoryService * history_service)155 void DomainDiversityReporter::HistoryServiceBeingDeleted(
156 history::HistoryService* history_service) {
157 history_service_observer_.RemoveAll();
158 cancelable_task_tracker_.TryCancelAll();
159 }
160