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 HandleNewConfig(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 } |
| 99 } |
| 100 |
| 101 // static |
| 102 DoodleService::DownloadOutcome DoodleService::DetermineDownloadOutcome( |
| 103 const base::Optional<DoodleConfig>& old_config, |
| 104 const base::Optional<DoodleConfig>& new_config, |
| 105 DoodleState state, |
| 106 bool expired) { |
| 107 // First, handle error cases: *_ERROR or EXPIRED override other outcomes. |
| 108 switch (state) { |
| 109 case DoodleState::AVAILABLE: |
| 110 if (expired) { |
| 111 return OUTCOME_EXPIRED; |
| 112 } |
| 113 break; |
| 114 |
| 115 case DoodleState::NO_DOODLE: |
| 116 break; |
| 117 |
| 118 case DoodleState::DOWNLOAD_ERROR: |
| 119 return OUTCOME_DOWNLOAD_ERROR; |
| 120 |
| 121 case DoodleState::PARSING_ERROR: |
| 122 return OUTCOME_PARSING_ERROR; |
| 123 } |
| 124 |
| 125 if (!new_config.has_value()) { |
| 126 return OUTCOME_NO_DOODLE; |
| 127 } |
| 128 if (!old_config.has_value()) { |
| 129 return OUTCOME_NEW_DOODLE; |
| 130 } |
| 131 if (old_config.value() != new_config.value()) { |
| 132 return OUTCOME_CHANGED_DOODLE; |
| 133 } |
| 134 return OUTCOME_REVALIDATED_DOODLE; |
63 } | 135 } |
64 | 136 |
65 void DoodleService::DoodleFetched( | 137 void DoodleService::DoodleFetched( |
| 138 base::TimeTicks start_time, |
66 DoodleState state, | 139 DoodleState state, |
67 base::TimeDelta time_to_live, | 140 base::TimeDelta time_to_live, |
68 const base::Optional<DoodleConfig>& doodle_config) { | 141 const base::Optional<DoodleConfig>& doodle_config) { |
69 UpdateCachedConfig(time_to_live, doodle_config); | 142 base::TimeDelta download_time = tick_clock_->NowTicks() - start_time; |
| 143 DownloadOutcome outcome = HandleNewConfig(state, time_to_live, doodle_config); |
| 144 RecordDownloadMetrics(outcome, download_time); |
70 } | 145 } |
71 | 146 |
72 void DoodleService::UpdateCachedConfig( | 147 DoodleService::DownloadOutcome DoodleService::HandleNewConfig( |
| 148 DoodleState state, |
73 base::TimeDelta time_to_live, | 149 base::TimeDelta time_to_live, |
74 const base::Optional<DoodleConfig>& doodle_config) { | 150 const base::Optional<DoodleConfig>& doodle_config) { |
75 // Handle the case where the new config is already expired. | 151 // Handle the case where the new config is already expired. |
76 bool expired = time_to_live <= base::TimeDelta(); | 152 bool expired = time_to_live <= base::TimeDelta(); |
77 const base::Optional<DoodleConfig>& new_config = | 153 const base::Optional<DoodleConfig>& new_config = |
78 expired ? base::nullopt : doodle_config; | 154 expired ? base::nullopt : doodle_config; |
79 | 155 |
| 156 // Determine the download outcome *before* updating the cached config. |
| 157 DownloadOutcome outcome = |
| 158 DetermineDownloadOutcome(cached_config_, new_config, state, expired); |
| 159 |
80 // If the config changed, update our cache and notify observers. | 160 // 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 | 161 // Note that this checks both for existence changes as well as changes of the |
82 // configs themselves. | 162 // configs themselves. |
83 if (cached_config_ != new_config) { | 163 if (cached_config_ != new_config) { |
84 cached_config_ = new_config; | 164 UpdateCachedConfig(time_to_live, new_config); |
85 | |
86 if (cached_config_.has_value()) { | |
87 pref_service_->Set(prefs::kCachedConfig, *cached_config_->ToDictionary()); | |
88 base::Time expiry_date = clock_->Now() + time_to_live; | |
89 pref_service_->SetInt64(prefs::kCachedConfigExpiry, | |
90 expiry_date.ToInternalValue()); | |
91 } else { | |
92 pref_service_->ClearPref(prefs::kCachedConfig); | |
93 pref_service_->ClearPref(prefs::kCachedConfigExpiry); | |
94 } | |
95 | 165 |
96 for (auto& observer : observers_) { | 166 for (auto& observer : observers_) { |
97 observer.OnDoodleConfigUpdated(cached_config_); | 167 observer.OnDoodleConfigUpdated(cached_config_); |
98 } | 168 } |
99 } | 169 } |
100 | 170 |
101 // Even if the configs are identical, the time-to-live might have changed. | 171 // Even if the configs are identical, the time-to-live might have changed. |
102 // (Re-)schedule the cache expiry. | 172 // (Re-)schedule the cache expiry. |
103 if (cached_config_.has_value()) { | 173 if (cached_config_.has_value()) { |
104 expiry_timer_->Start( | 174 expiry_timer_->Start( |
105 FROM_HERE, time_to_live, | 175 FROM_HERE, time_to_live, |
106 base::Bind(&DoodleService::DoodleExpired, base::Unretained(this))); | 176 base::Bind(&DoodleService::DoodleExpired, base::Unretained(this))); |
107 } else { | 177 } else { |
108 expiry_timer_->Stop(); | 178 expiry_timer_->Stop(); |
109 } | 179 } |
| 180 |
| 181 return outcome; |
| 182 } |
| 183 |
| 184 void DoodleService::UpdateCachedConfig( |
| 185 base::TimeDelta time_to_live, |
| 186 const base::Optional<DoodleConfig>& new_config) { |
| 187 DCHECK(cached_config_ != new_config); |
| 188 |
| 189 cached_config_ = new_config; |
| 190 |
| 191 if (cached_config_.has_value()) { |
| 192 pref_service_->Set(prefs::kCachedConfig, *cached_config_->ToDictionary()); |
| 193 base::Time expiry_date = clock_->Now() + time_to_live; |
| 194 pref_service_->SetInt64(prefs::kCachedConfigExpiry, |
| 195 expiry_date.ToInternalValue()); |
| 196 } else { |
| 197 pref_service_->ClearPref(prefs::kCachedConfig); |
| 198 pref_service_->ClearPref(prefs::kCachedConfigExpiry); |
| 199 } |
110 } | 200 } |
111 | 201 |
112 void DoodleService::DoodleExpired() { | 202 void DoodleService::DoodleExpired() { |
113 DCHECK(cached_config_.has_value()); | 203 DCHECK(cached_config_.has_value()); |
114 UpdateCachedConfig(base::TimeDelta(), base::nullopt); | 204 HandleNewConfig(DoodleState::NO_DOODLE, base::TimeDelta(), base::nullopt); |
115 } | 205 } |
116 | 206 |
117 } // namespace doodle | 207 } // namespace doodle |
OLD | NEW |