OLD | NEW |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/doodle/doodle_service.h" | 5 #include "components/doodle/doodle_service.h" |
6 | 6 |
7 #include <utility> | 7 #include <utility> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/metrics/histogram_macros.h" |
10 #include "base/time/time.h" | 11 #include "base/time/time.h" |
11 #include "base/values.h" | 12 #include "base/values.h" |
12 #include "components/doodle/pref_names.h" | 13 #include "components/doodle/pref_names.h" |
13 #include "components/prefs/pref_registry.h" | 14 #include "components/prefs/pref_registry.h" |
14 #include "components/prefs/pref_registry_simple.h" | 15 #include "components/prefs/pref_registry_simple.h" |
15 #include "components/prefs/pref_service.h" | 16 #include "components/prefs/pref_service.h" |
16 | 17 |
17 namespace doodle { | 18 namespace doodle { |
18 | 19 |
19 // static | 20 // static |
20 void DoodleService::RegisterProfilePrefs(PrefRegistrySimple* pref_registry) { | 21 void DoodleService::RegisterProfilePrefs(PrefRegistrySimple* pref_registry) { |
21 pref_registry->RegisterDictionaryPref(prefs::kCachedConfig, | 22 pref_registry->RegisterDictionaryPref(prefs::kCachedConfig, |
22 new base::DictionaryValue(), | 23 new base::DictionaryValue(), |
23 PrefRegistry::LOSSY_PREF); | 24 PrefRegistry::LOSSY_PREF); |
24 pref_registry->RegisterInt64Pref(prefs::kCachedConfigExpiry, 0, | 25 pref_registry->RegisterInt64Pref(prefs::kCachedConfigExpiry, 0, |
25 PrefRegistry::LOSSY_PREF); | 26 PrefRegistry::LOSSY_PREF); |
26 } | 27 } |
27 | 28 |
28 DoodleService::DoodleService(PrefService* pref_service, | 29 DoodleService::DoodleService(PrefService* pref_service, |
29 std::unique_ptr<DoodleFetcher> fetcher, | 30 std::unique_ptr<DoodleFetcher> fetcher, |
30 std::unique_ptr<base::OneShotTimer> expiry_timer, | 31 std::unique_ptr<base::OneShotTimer> expiry_timer, |
31 std::unique_ptr<base::Clock> clock) | 32 std::unique_ptr<base::Clock> clock, |
| 33 std::unique_ptr<base::TickClock> tick_clock) |
32 : pref_service_(pref_service), | 34 : pref_service_(pref_service), |
33 fetcher_(std::move(fetcher)), | 35 fetcher_(std::move(fetcher)), |
34 expiry_timer_(std::move(expiry_timer)), | 36 expiry_timer_(std::move(expiry_timer)), |
35 clock_(std::move(clock)) { | 37 clock_(std::move(clock)), |
| 38 tick_clock_(std::move(tick_clock)) { |
36 DCHECK(pref_service_); | 39 DCHECK(pref_service_); |
37 DCHECK(fetcher_); | 40 DCHECK(fetcher_); |
38 DCHECK(expiry_timer_); | 41 DCHECK(expiry_timer_); |
39 DCHECK(clock_); | 42 DCHECK(clock_); |
| 43 DCHECK(tick_clock_); |
40 | 44 |
41 base::Time expiry_date = base::Time::FromInternalValue( | 45 base::Time expiry_date = base::Time::FromInternalValue( |
42 pref_service_->GetInt64(prefs::kCachedConfigExpiry)); | 46 pref_service_->GetInt64(prefs::kCachedConfigExpiry)); |
43 base::Optional<DoodleConfig> config = DoodleConfig::FromDictionary( | 47 base::Optional<DoodleConfig> config = DoodleConfig::FromDictionary( |
44 *pref_service_->GetDictionary(prefs::kCachedConfig), base::nullopt); | 48 *pref_service_->GetDictionary(prefs::kCachedConfig), base::nullopt); |
45 UpdateCachedConfig(expiry_date - clock_->Now(), config); | 49 DoodleState state = |
| 50 config.has_value() ? DoodleState::AVAILABLE : DoodleState::NO_DOODLE; |
| 51 UpdateCachedConfig(state, expiry_date - clock_->Now(), config); |
46 } | 52 } |
47 | 53 |
48 DoodleService::~DoodleService() = default; | 54 DoodleService::~DoodleService() = default; |
49 | 55 |
50 void DoodleService::Shutdown() {} | 56 void DoodleService::Shutdown() {} |
51 | 57 |
52 void DoodleService::AddObserver(Observer* observer) { | 58 void DoodleService::AddObserver(Observer* observer) { |
53 observers_.AddObserver(observer); | 59 observers_.AddObserver(observer); |
54 } | 60 } |
55 | 61 |
56 void DoodleService::RemoveObserver(Observer* observer) { | 62 void DoodleService::RemoveObserver(Observer* observer) { |
57 observers_.RemoveObserver(observer); | 63 observers_.RemoveObserver(observer); |
58 } | 64 } |
59 | 65 |
60 void DoodleService::Refresh() { | 66 void DoodleService::Refresh() { |
61 fetcher_->FetchDoodle( | 67 fetcher_->FetchDoodle(base::BindOnce(&DoodleService::DoodleFetched, |
62 base::BindOnce(&DoodleService::DoodleFetched, base::Unretained(this))); | 68 base::Unretained(this), |
| 69 tick_clock_->NowTicks())); |
| 70 } |
| 71 |
| 72 // static |
| 73 bool DoodleService::DownloadOutcomeIsSuccess(DownloadOutcome outcome) { |
| 74 switch (outcome) { |
| 75 case OUTCOME_NEW_DOODLE: |
| 76 case OUTCOME_REVALIDATED_DOODLE: |
| 77 case OUTCOME_CHANGED_DOODLE: |
| 78 case OUTCOME_NO_DOODLE: |
| 79 return true; |
| 80 case OUTCOME_EXPIRED: |
| 81 case OUTCOME_DOWNLOAD_ERROR: |
| 82 case OUTCOME_PARSING_ERROR: |
| 83 return false; |
| 84 case OUTCOME_COUNT: |
| 85 NOTREACHED(); |
| 86 } |
| 87 return false; |
| 88 } |
| 89 |
| 90 // static |
| 91 void DoodleService::RecordDownloadMetrics(DownloadOutcome outcome, |
| 92 base::TimeDelta download_time) { |
| 93 UMA_HISTOGRAM_ENUMERATION("Doodle.ConfigDownloadOutcome", outcome, |
| 94 OUTCOME_COUNT); |
| 95 |
| 96 if (DownloadOutcomeIsSuccess(outcome)) { |
| 97 UMA_HISTOGRAM_MEDIUM_TIMES("Doodle.ConfigDownloadTime", download_time); |
| 98 } |
63 } | 99 } |
64 | 100 |
65 void DoodleService::DoodleFetched( | 101 void DoodleService::DoodleFetched( |
| 102 base::TimeTicks start_time, |
66 DoodleState state, | 103 DoodleState state, |
67 base::TimeDelta time_to_live, | 104 base::TimeDelta time_to_live, |
68 const base::Optional<DoodleConfig>& doodle_config) { | 105 const base::Optional<DoodleConfig>& doodle_config) { |
69 UpdateCachedConfig(time_to_live, doodle_config); | 106 base::TimeDelta download_time = tick_clock_->NowTicks() - start_time; |
| 107 DownloadOutcome outcome = |
| 108 UpdateCachedConfig(state, time_to_live, doodle_config); |
| 109 RecordDownloadMetrics(outcome, download_time); |
70 } | 110 } |
71 | 111 |
72 void DoodleService::UpdateCachedConfig( | 112 DoodleService::DownloadOutcome DoodleService::UpdateCachedConfig( |
| 113 DoodleState state, |
73 base::TimeDelta time_to_live, | 114 base::TimeDelta time_to_live, |
74 const base::Optional<DoodleConfig>& doodle_config) { | 115 const base::Optional<DoodleConfig>& doodle_config) { |
75 // Handle the case where the new config is already expired. | 116 // Handle the case where the new config is already expired. |
76 bool expired = time_to_live <= base::TimeDelta(); | 117 bool expired = time_to_live <= base::TimeDelta(); |
77 const base::Optional<DoodleConfig>& new_config = | 118 const base::Optional<DoodleConfig>& new_config = |
78 expired ? base::nullopt : doodle_config; | 119 expired ? base::nullopt : doodle_config; |
79 | 120 |
| 121 DownloadOutcome outcome = OUTCOME_COUNT; |
| 122 |
80 // If the config changed, update our cache and notify observers. | 123 // If the config changed, update our cache and notify observers. |
81 // Note that this checks both for existence changes as well as changes of the | 124 // Note that this checks both for existence changes as well as changes of the |
82 // configs themselves. | 125 // configs themselves. |
83 if (cached_config_ != new_config) { | 126 if (cached_config_ != new_config) { |
| 127 bool had_config_before = cached_config_.has_value(); |
| 128 |
84 cached_config_ = new_config; | 129 cached_config_ = new_config; |
85 | 130 |
86 if (cached_config_.has_value()) { | 131 if (cached_config_.has_value()) { |
| 132 outcome = had_config_before ? OUTCOME_CHANGED_DOODLE : OUTCOME_NEW_DOODLE; |
| 133 |
87 pref_service_->Set(prefs::kCachedConfig, *cached_config_->ToDictionary()); | 134 pref_service_->Set(prefs::kCachedConfig, *cached_config_->ToDictionary()); |
88 base::Time expiry_date = clock_->Now() + time_to_live; | 135 base::Time expiry_date = clock_->Now() + time_to_live; |
89 pref_service_->SetInt64(prefs::kCachedConfigExpiry, | 136 pref_service_->SetInt64(prefs::kCachedConfigExpiry, |
90 expiry_date.ToInternalValue()); | 137 expiry_date.ToInternalValue()); |
91 } else { | 138 } else { |
| 139 outcome = OUTCOME_NO_DOODLE; |
| 140 |
92 pref_service_->ClearPref(prefs::kCachedConfig); | 141 pref_service_->ClearPref(prefs::kCachedConfig); |
93 pref_service_->ClearPref(prefs::kCachedConfigExpiry); | 142 pref_service_->ClearPref(prefs::kCachedConfigExpiry); |
94 } | 143 } |
95 | 144 |
96 for (auto& observer : observers_) { | 145 for (auto& observer : observers_) { |
97 observer.OnDoodleConfigUpdated(cached_config_); | 146 observer.OnDoodleConfigUpdated(cached_config_); |
98 } | 147 } |
| 148 } else { |
| 149 outcome = cached_config_.has_value() ? OUTCOME_REVALIDATED_DOODLE |
| 150 : OUTCOME_NO_DOODLE; |
| 151 } |
| 152 |
| 153 DCHECK(outcome != OUTCOME_COUNT); |
| 154 // Determine the final outcome. ERROR or EXPIRED override NO_DOODLE. |
| 155 switch (state) { |
| 156 case DoodleState::AVAILABLE: |
| 157 case DoodleState::NO_DOODLE: |
| 158 if (expired) { |
| 159 outcome = OUTCOME_EXPIRED; |
| 160 } |
| 161 break; |
| 162 |
| 163 case DoodleState::DOWNLOAD_ERROR: |
| 164 outcome = OUTCOME_DOWNLOAD_ERROR; |
| 165 break; |
| 166 |
| 167 case DoodleState::PARSING_ERROR: |
| 168 outcome = OUTCOME_PARSING_ERROR; |
| 169 break; |
99 } | 170 } |
100 | 171 |
101 // Even if the configs are identical, the time-to-live might have changed. | 172 // Even if the configs are identical, the time-to-live might have changed. |
102 // (Re-)schedule the cache expiry. | 173 // (Re-)schedule the cache expiry. |
103 if (cached_config_.has_value()) { | 174 if (cached_config_.has_value()) { |
104 expiry_timer_->Start( | 175 expiry_timer_->Start( |
105 FROM_HERE, time_to_live, | 176 FROM_HERE, time_to_live, |
106 base::Bind(&DoodleService::DoodleExpired, base::Unretained(this))); | 177 base::Bind(&DoodleService::DoodleExpired, base::Unretained(this))); |
107 } else { | 178 } else { |
108 expiry_timer_->Stop(); | 179 expiry_timer_->Stop(); |
109 } | 180 } |
| 181 |
| 182 return outcome; |
110 } | 183 } |
111 | 184 |
112 void DoodleService::DoodleExpired() { | 185 void DoodleService::DoodleExpired() { |
113 DCHECK(cached_config_.has_value()); | 186 DCHECK(cached_config_.has_value()); |
114 UpdateCachedConfig(base::TimeDelta(), base::nullopt); | 187 UpdateCachedConfig(DoodleState::NO_DOODLE, base::TimeDelta(), base::nullopt); |
115 } | 188 } |
116 | 189 |
117 } // namespace doodle | 190 } // namespace doodle |
OLD | NEW |