| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 "chrome/browser/budget_service/budget_database.h" | 5 #include "chrome/browser/budget_service/budget_database.h" |
| 6 | 6 |
| 7 #include "base/metrics/histogram_macros.h" | 7 #include "base/metrics/histogram_macros.h" |
| 8 #include "base/time/clock.h" | 8 #include "base/time/clock.h" |
| 9 #include "base/time/default_clock.h" | 9 #include "base/time/default_clock.h" |
| 10 #include "chrome/browser/budget_service/budget.pb.h" | 10 #include "chrome/browser/budget_service/budget.pb.h" |
| 11 #include "chrome/browser/engagement/site_engagement_score.h" | 11 #include "chrome/browser/engagement/site_engagement_score.h" |
| 12 #include "chrome/browser/engagement/site_engagement_service.h" | 12 #include "chrome/browser/engagement/site_engagement_service.h" |
| 13 #include "chrome/browser/profiles/profile.h" | 13 #include "chrome/browser/profiles/profile.h" |
| 14 #include "components/leveldb_proto/proto_database_impl.h" | 14 #include "components/leveldb_proto/proto_database_impl.h" |
| 15 #include "content/public/browser/browser_thread.h" | 15 #include "content/public/browser/browser_thread.h" |
| 16 #include "url/gurl.h" | 16 #include "url/gurl.h" |
| 17 #include "url/origin.h" |
| 17 | 18 |
| 18 using content::BrowserThread; | 19 using content::BrowserThread; |
| 19 | 20 |
| 20 namespace { | 21 namespace { |
| 21 | 22 |
| 22 // UMA are logged for the database with this string as part of the name. | 23 // UMA are logged for the database with this string as part of the name. |
| 23 // They will be LevelDB.*.BudgetManager. Changes here should be synchronized | 24 // They will be LevelDB.*.BudgetManager. Changes here should be synchronized |
| 24 // with histograms.xml. | 25 // with histograms.xml. |
| 25 const char kDatabaseUMAName[] = "BudgetManager"; | 26 const char kDatabaseUMAName[] = "BudgetManager"; |
| 26 | 27 |
| (...skipping 21 matching lines...) Expand all Loading... |
| 48 task_runner)), | 49 task_runner)), |
| 49 clock_(base::WrapUnique(new base::DefaultClock)), | 50 clock_(base::WrapUnique(new base::DefaultClock)), |
| 50 weak_ptr_factory_(this) { | 51 weak_ptr_factory_(this) { |
| 51 db_->Init(kDatabaseUMAName, database_dir, | 52 db_->Init(kDatabaseUMAName, database_dir, |
| 52 base::Bind(&BudgetDatabase::OnDatabaseInit, | 53 base::Bind(&BudgetDatabase::OnDatabaseInit, |
| 53 weak_ptr_factory_.GetWeakPtr())); | 54 weak_ptr_factory_.GetWeakPtr())); |
| 54 } | 55 } |
| 55 | 56 |
| 56 BudgetDatabase::~BudgetDatabase() {} | 57 BudgetDatabase::~BudgetDatabase() {} |
| 57 | 58 |
| 58 void BudgetDatabase::GetBudgetDetails(const GURL& origin, | 59 void BudgetDatabase::GetBudgetDetails(const url::Origin& origin, |
| 59 const GetBudgetCallback& callback) { | 60 const GetBudgetCallback& callback) { |
| 60 DCHECK_EQ(origin.GetOrigin(), origin); | |
| 61 | |
| 62 SyncCache(origin, | 61 SyncCache(origin, |
| 63 base::Bind(&BudgetDatabase::GetBudgetAfterSync, | 62 base::Bind(&BudgetDatabase::GetBudgetAfterSync, |
| 64 weak_ptr_factory_.GetWeakPtr(), origin, callback)); | 63 weak_ptr_factory_.GetWeakPtr(), origin, callback)); |
| 65 } | 64 } |
| 66 | 65 |
| 67 void BudgetDatabase::SpendBudget(const GURL& origin, | 66 void BudgetDatabase::SpendBudget(const url::Origin& origin, |
| 68 double amount, | 67 double amount, |
| 69 const StoreBudgetCallback& callback) { | 68 const StoreBudgetCallback& callback) { |
| 70 SyncCache(origin, base::Bind(&BudgetDatabase::SpendBudgetAfterSync, | 69 SyncCache(origin, base::Bind(&BudgetDatabase::SpendBudgetAfterSync, |
| 71 weak_ptr_factory_.GetWeakPtr(), origin, amount, | 70 weak_ptr_factory_.GetWeakPtr(), origin, amount, |
| 72 callback)); | 71 callback)); |
| 73 } | 72 } |
| 74 | 73 |
| 75 void BudgetDatabase::SetClockForTesting(std::unique_ptr<base::Clock> clock) { | 74 void BudgetDatabase::SetClockForTesting(std::unique_ptr<base::Clock> clock) { |
| 76 clock_ = std::move(clock); | 75 clock_ = std::move(clock); |
| 77 } | 76 } |
| 78 | 77 |
| 79 void BudgetDatabase::OnDatabaseInit(bool success) { | 78 void BudgetDatabase::OnDatabaseInit(bool success) { |
| 80 // TODO(harkness): Consider caching the budget database now? | 79 // TODO(harkness): Consider caching the budget database now? |
| 81 } | 80 } |
| 82 | 81 |
| 83 bool BudgetDatabase::IsCached(const GURL& origin) const { | 82 bool BudgetDatabase::IsCached(const url::Origin& origin) const { |
| 84 return budget_map_.find(origin.spec()) != budget_map_.end(); | 83 return budget_map_.find(origin) != budget_map_.end(); |
| 85 } | 84 } |
| 86 | 85 |
| 87 double BudgetDatabase::GetBudget(const GURL& origin) const { | 86 double BudgetDatabase::GetBudget(const url::Origin& origin) const { |
| 88 double total = 0; | 87 double total = 0; |
| 89 auto iter = budget_map_.find(origin.spec()); | 88 auto iter = budget_map_.find(origin); |
| 90 if (iter == budget_map_.end()) | 89 if (iter == budget_map_.end()) |
| 91 return total; | 90 return total; |
| 92 | 91 |
| 93 const BudgetInfo& info = iter->second; | 92 const BudgetInfo& info = iter->second; |
| 94 for (const BudgetChunk& chunk : info.chunks) | 93 for (const BudgetChunk& chunk : info.chunks) |
| 95 total += chunk.amount; | 94 total += chunk.amount; |
| 96 return total; | 95 return total; |
| 97 } | 96 } |
| 98 | 97 |
| 99 void BudgetDatabase::AddToCache( | 98 void BudgetDatabase::AddToCache( |
| 100 const GURL& origin, | 99 const url::Origin& origin, |
| 101 const AddToCacheCallback& callback, | 100 const AddToCacheCallback& callback, |
| 102 bool success, | 101 bool success, |
| 103 std::unique_ptr<budget_service::Budget> budget_proto) { | 102 std::unique_ptr<budget_service::Budget> budget_proto) { |
| 104 // If the database read failed, there's nothing to add to the cache. | 103 // If the database read failed, there's nothing to add to the cache. |
| 105 if (!success || !budget_proto) { | 104 if (!success || !budget_proto) { |
| 106 callback.Run(success); | 105 callback.Run(success); |
| 107 return; | 106 return; |
| 108 } | 107 } |
| 109 | 108 |
| 110 // If there were two simultaneous loads, don't overwrite the cache value, | 109 // If there were two simultaneous loads, don't overwrite the cache value, |
| 111 // which might have been updated after the previous load. | 110 // which might have been updated after the previous load. |
| 112 if (IsCached(origin)) { | 111 if (IsCached(origin)) { |
| 113 callback.Run(success); | 112 callback.Run(success); |
| 114 return; | 113 return; |
| 115 } | 114 } |
| 116 | 115 |
| 117 // Add the data to the cache, converting from the proto format to an STL | 116 // Add the data to the cache, converting from the proto format to an STL |
| 118 // format which is better for removing things from the list. | 117 // format which is better for removing things from the list. |
| 119 BudgetInfo& info = budget_map_[origin.spec()]; | 118 BudgetInfo& info = budget_map_[origin]; |
| 120 for (const auto& chunk : budget_proto->budget()) { | 119 for (const auto& chunk : budget_proto->budget()) { |
| 121 info.chunks.emplace_back(chunk.amount(), | 120 info.chunks.emplace_back(chunk.amount(), |
| 122 base::Time::FromInternalValue(chunk.expiration())); | 121 base::Time::FromInternalValue(chunk.expiration())); |
| 123 } | 122 } |
| 124 | 123 |
| 125 info.last_engagement_award = | 124 info.last_engagement_award = |
| 126 base::Time::FromInternalValue(budget_proto->engagement_last_updated()); | 125 base::Time::FromInternalValue(budget_proto->engagement_last_updated()); |
| 127 | 126 |
| 128 callback.Run(success); | 127 callback.Run(success); |
| 129 } | 128 } |
| 130 | 129 |
| 131 void BudgetDatabase::GetBudgetAfterSync(const GURL& origin, | 130 void BudgetDatabase::GetBudgetAfterSync(const url::Origin& origin, |
| 132 const GetBudgetCallback& callback, | 131 const GetBudgetCallback& callback, |
| 133 bool success) { | 132 bool success) { |
| 134 mojo::Array<blink::mojom::BudgetStatePtr> predictions; | 133 mojo::Array<blink::mojom::BudgetStatePtr> predictions; |
| 135 | 134 |
| 136 // If the database wasn't able to read the information, return the | 135 // If the database wasn't able to read the information, return the |
| 137 // failure and an empty predictions array. | 136 // failure and an empty predictions array. |
| 138 if (!success) { | 137 if (!success) { |
| 139 callback.Run(blink::mojom::BudgetServiceErrorType::DATABASE_ERROR, | 138 callback.Run(blink::mojom::BudgetServiceErrorType::DATABASE_ERROR, |
| 140 std::move(predictions)); | 139 std::move(predictions)); |
| 141 return; | 140 return; |
| 142 } | 141 } |
| 143 | 142 |
| 144 // Now, build up the BudgetExpection. This is different from the format | 143 // Now, build up the BudgetExpection. This is different from the format |
| 145 // in which the cache stores the data. The cache stores chunks of budget and | 144 // in which the cache stores the data. The cache stores chunks of budget and |
| 146 // when that budget expires. The mojo array describes a set of times | 145 // when that budget expires. The mojo array describes a set of times |
| 147 // and the budget at those times. | 146 // and the budget at those times. |
| 148 double total = GetBudget(origin); | 147 double total = GetBudget(origin); |
| 149 | 148 |
| 150 // Always add one entry at the front of the list for the total budget now. | 149 // Always add one entry at the front of the list for the total budget now. |
| 151 blink::mojom::BudgetStatePtr prediction(blink::mojom::BudgetState::New()); | 150 blink::mojom::BudgetStatePtr prediction(blink::mojom::BudgetState::New()); |
| 152 prediction->budget_at = total; | 151 prediction->budget_at = total; |
| 153 prediction->time = clock_->Now().ToDoubleT(); | 152 prediction->time = clock_->Now().ToDoubleT(); |
| 154 predictions.push_back(std::move(prediction)); | 153 predictions.push_back(std::move(prediction)); |
| 155 | 154 |
| 156 // Starting with the soonest expiring chunks, add entries for the | 155 // Starting with the soonest expiring chunks, add entries for the |
| 157 // expiration times going forward. | 156 // expiration times going forward. |
| 158 const BudgetChunks& chunks = budget_map_[origin.spec()].chunks; | 157 const BudgetChunks& chunks = budget_map_[origin].chunks; |
| 159 for (const auto& chunk : chunks) { | 158 for (const auto& chunk : chunks) { |
| 160 blink::mojom::BudgetStatePtr prediction(blink::mojom::BudgetState::New()); | 159 blink::mojom::BudgetStatePtr prediction(blink::mojom::BudgetState::New()); |
| 161 total -= chunk.amount; | 160 total -= chunk.amount; |
| 162 prediction->budget_at = total; | 161 prediction->budget_at = total; |
| 163 prediction->time = chunk.expiration.ToDoubleT(); | 162 prediction->time = chunk.expiration.ToDoubleT(); |
| 164 predictions.push_back(std::move(prediction)); | 163 predictions.push_back(std::move(prediction)); |
| 165 } | 164 } |
| 166 | 165 |
| 167 DCHECK_EQ(0, total); | 166 DCHECK_EQ(0, total); |
| 168 | 167 |
| 169 callback.Run(blink::mojom::BudgetServiceErrorType::NONE, | 168 callback.Run(blink::mojom::BudgetServiceErrorType::NONE, |
| 170 std::move(predictions)); | 169 std::move(predictions)); |
| 171 } | 170 } |
| 172 | 171 |
| 173 void BudgetDatabase::SpendBudgetAfterSync(const GURL& origin, | 172 void BudgetDatabase::SpendBudgetAfterSync(const url::Origin& origin, |
| 174 double amount, | 173 double amount, |
| 175 const StoreBudgetCallback& callback, | 174 const StoreBudgetCallback& callback, |
| 176 bool success) { | 175 bool success) { |
| 177 if (!success) { | 176 if (!success) { |
| 178 callback.Run(false /* success */); | 177 callback.Run(false /* success */); |
| 179 return; | 178 return; |
| 180 } | 179 } |
| 181 | 180 |
| 182 // Get the current SES score, to generate UMA. | 181 // Get the current SES score, to generate UMA. |
| 183 SiteEngagementService* service = SiteEngagementService::Get(profile_); | 182 SiteEngagementService* service = SiteEngagementService::Get(profile_); |
| 184 double score = service->GetScore(origin); | 183 double score = service->GetScore(GURL(origin.Serialize())); |
| 185 | 184 |
| 186 // Walk the list of budget chunks to see if the origin has enough budget. | 185 // Walk the list of budget chunks to see if the origin has enough budget. |
| 187 double total = 0; | 186 double total = 0; |
| 188 BudgetInfo& info = budget_map_[origin.spec()]; | 187 BudgetInfo& info = budget_map_[origin]; |
| 189 for (const BudgetChunk& chunk : info.chunks) | 188 for (const BudgetChunk& chunk : info.chunks) |
| 190 total += chunk.amount; | 189 total += chunk.amount; |
| 191 | 190 |
| 192 if (total < amount) { | 191 if (total < amount) { |
| 193 UMA_HISTOGRAM_COUNTS_100("PushMessaging.SESForNoBudgetOrigin", score); | 192 UMA_HISTOGRAM_COUNTS_100("PushMessaging.SESForNoBudgetOrigin", score); |
| 194 callback.Run(false /* success */); | 193 callback.Run(false /* success */); |
| 195 return; | 194 return; |
| 196 } else if (total < amount * 2) { | 195 } else if (total < amount * 2) { |
| 197 UMA_HISTOGRAM_COUNTS_100("PushMessaging.SESForLowBudgetOrigin", score); | 196 UMA_HISTOGRAM_COUNTS_100("PushMessaging.SESForLowBudgetOrigin", score); |
| 198 } | 197 } |
| (...skipping 16 matching lines...) Expand all Loading... |
| 215 // Now that the cache is updated, write the data to the database. | 214 // Now that the cache is updated, write the data to the database. |
| 216 // TODO(harkness): Consider adding a second parameter to the callback so the | 215 // TODO(harkness): Consider adding a second parameter to the callback so the |
| 217 // caller can distinguish between not enough budget and a failed database | 216 // caller can distinguish between not enough budget and a failed database |
| 218 // write. | 217 // write. |
| 219 // TODO(harkness): If the database write fails, the cache will be out of sync | 218 // TODO(harkness): If the database write fails, the cache will be out of sync |
| 220 // with the database. Consider ways to mitigate this. | 219 // with the database. Consider ways to mitigate this. |
| 221 WriteCachedValuesToDatabase(origin, callback); | 220 WriteCachedValuesToDatabase(origin, callback); |
| 222 } | 221 } |
| 223 | 222 |
| 224 void BudgetDatabase::WriteCachedValuesToDatabase( | 223 void BudgetDatabase::WriteCachedValuesToDatabase( |
| 225 const GURL& origin, | 224 const url::Origin& origin, |
| 226 const StoreBudgetCallback& callback) { | 225 const StoreBudgetCallback& callback) { |
| 227 // Create the data structures that are passed to the ProtoDatabase. | 226 // Create the data structures that are passed to the ProtoDatabase. |
| 228 std::unique_ptr< | 227 std::unique_ptr< |
| 229 leveldb_proto::ProtoDatabase<budget_service::Budget>::KeyEntryVector> | 228 leveldb_proto::ProtoDatabase<budget_service::Budget>::KeyEntryVector> |
| 230 entries(new leveldb_proto::ProtoDatabase< | 229 entries(new leveldb_proto::ProtoDatabase< |
| 231 budget_service::Budget>::KeyEntryVector()); | 230 budget_service::Budget>::KeyEntryVector()); |
| 232 std::unique_ptr<std::vector<std::string>> keys_to_remove( | 231 std::unique_ptr<std::vector<std::string>> keys_to_remove( |
| 233 new std::vector<std::string>()); | 232 new std::vector<std::string>()); |
| 234 | 233 |
| 235 // Each operation can either update the existing budget or remove the origin's | 234 // Each operation can either update the existing budget or remove the origin's |
| 236 // budget information. | 235 // budget information. |
| 237 if (IsCached(origin)) { | 236 if (IsCached(origin)) { |
| 238 // Build the Budget proto object. | 237 // Build the Budget proto object. |
| 239 budget_service::Budget budget; | 238 budget_service::Budget budget; |
| 240 const BudgetInfo& info = budget_map_[origin.spec()]; | 239 const BudgetInfo& info = budget_map_[origin]; |
| 241 for (const auto& chunk : info.chunks) { | 240 for (const auto& chunk : info.chunks) { |
| 242 budget_service::BudgetChunk* budget_chunk = budget.add_budget(); | 241 budget_service::BudgetChunk* budget_chunk = budget.add_budget(); |
| 243 budget_chunk->set_amount(chunk.amount); | 242 budget_chunk->set_amount(chunk.amount); |
| 244 budget_chunk->set_expiration(chunk.expiration.ToInternalValue()); | 243 budget_chunk->set_expiration(chunk.expiration.ToInternalValue()); |
| 245 } | 244 } |
| 246 budget.set_engagement_last_updated( | 245 budget.set_engagement_last_updated( |
| 247 info.last_engagement_award.ToInternalValue()); | 246 info.last_engagement_award.ToInternalValue()); |
| 248 entries->push_back(std::make_pair(origin.spec(), budget)); | 247 entries->push_back(std::make_pair(origin.Serialize(), budget)); |
| 249 } else { | 248 } else { |
| 250 // If the origin doesn't exist in the cache, this is a remove operation. | 249 // If the origin doesn't exist in the cache, this is a remove operation. |
| 251 keys_to_remove->push_back(origin.spec()); | 250 keys_to_remove->push_back(origin.Serialize()); |
| 252 } | 251 } |
| 253 | 252 |
| 254 // Send the updates to the database. | 253 // Send the updates to the database. |
| 255 db_->UpdateEntries(std::move(entries), std::move(keys_to_remove), callback); | 254 db_->UpdateEntries(std::move(entries), std::move(keys_to_remove), callback); |
| 256 } | 255 } |
| 257 | 256 |
| 258 void BudgetDatabase::SyncCache(const GURL& origin, | 257 void BudgetDatabase::SyncCache(const url::Origin& origin, |
| 259 const SyncCacheCallback& callback) { | 258 const SyncCacheCallback& callback) { |
| 260 DCHECK_EQ(origin, origin.GetOrigin()); | |
| 261 | |
| 262 // If the origin isn't already cached, add it to the cache. | 259 // If the origin isn't already cached, add it to the cache. |
| 263 if (!IsCached(origin)) { | 260 if (!IsCached(origin)) { |
| 264 AddToCacheCallback add_callback = | 261 AddToCacheCallback add_callback = |
| 265 base::Bind(&BudgetDatabase::SyncLoadedCache, | 262 base::Bind(&BudgetDatabase::SyncLoadedCache, |
| 266 weak_ptr_factory_.GetWeakPtr(), origin, callback); | 263 weak_ptr_factory_.GetWeakPtr(), origin, callback); |
| 267 db_->GetEntry(origin.spec(), base::Bind(&BudgetDatabase::AddToCache, | 264 db_->GetEntry(origin.Serialize(), base::Bind(&BudgetDatabase::AddToCache, |
| 268 weak_ptr_factory_.GetWeakPtr(), | 265 weak_ptr_factory_.GetWeakPtr(), |
| 269 origin, add_callback)); | 266 origin, add_callback)); |
| 270 return; | 267 return; |
| 271 } | 268 } |
| 272 SyncLoadedCache(origin, callback, true /* success */); | 269 SyncLoadedCache(origin, callback, true /* success */); |
| 273 } | 270 } |
| 274 | 271 |
| 275 void BudgetDatabase::SyncLoadedCache(const GURL& origin, | 272 void BudgetDatabase::SyncLoadedCache(const url::Origin& origin, |
| 276 const SyncCacheCallback& callback, | 273 const SyncCacheCallback& callback, |
| 277 bool success) { | 274 bool success) { |
| 278 if (!success) { | 275 if (!success) { |
| 279 callback.Run(false /* success */); | 276 callback.Run(false /* success */); |
| 280 return; | 277 return; |
| 281 } | 278 } |
| 282 | 279 |
| 283 // Now, cleanup any expired budget chunks for the origin. | 280 // Now, cleanup any expired budget chunks for the origin. |
| 284 bool needs_write = CleanupExpiredBudget(origin); | 281 bool needs_write = CleanupExpiredBudget(origin); |
| 285 | 282 |
| 286 // Get the SES score and add engagement budget for the site. | 283 // Get the SES score and add engagement budget for the site. |
| 287 AddEngagementBudget(origin); | 284 AddEngagementBudget(origin); |
| 288 | 285 |
| 289 if (needs_write) | 286 if (needs_write) |
| 290 WriteCachedValuesToDatabase(origin, callback); | 287 WriteCachedValuesToDatabase(origin, callback); |
| 291 else | 288 else |
| 292 callback.Run(success); | 289 callback.Run(success); |
| 293 } | 290 } |
| 294 | 291 |
| 295 void BudgetDatabase::AddEngagementBudget(const GURL& origin) { | 292 void BudgetDatabase::AddEngagementBudget(const url::Origin& origin) { |
| 296 // Get the current SES score, which we'll use to set a new budget. | 293 // Get the current SES score, which we'll use to set a new budget. |
| 297 SiteEngagementService* service = SiteEngagementService::Get(profile_); | 294 SiteEngagementService* service = SiteEngagementService::Get(profile_); |
| 298 double score = service->GetScore(origin); | 295 double score = service->GetScore(GURL(origin.Serialize())); |
| 299 | 296 |
| 300 // By default we award the "full" award. Then that ratio is decreased if | 297 // By default we award the "full" award. Then that ratio is decreased if |
| 301 // there have been other awards recently. | 298 // there have been other awards recently. |
| 302 double ratio = 1.0; | 299 double ratio = 1.0; |
| 303 | 300 |
| 304 // Calculate how much budget should be awarded. If there is no entry in the | 301 // Calculate how much budget should be awarded. If there is no entry in the |
| 305 // cache then we award a full amount. | 302 // cache then we award a full amount. |
| 306 if (IsCached(origin)) { | 303 if (IsCached(origin)) { |
| 307 base::TimeDelta elapsed = | 304 base::TimeDelta elapsed = |
| 308 clock_->Now() - budget_map_[origin.spec()].last_engagement_award; | 305 clock_->Now() - budget_map_[origin].last_engagement_award; |
| 309 int elapsed_hours = elapsed.InHours(); | 306 int elapsed_hours = elapsed.InHours(); |
| 310 // Don't give engagement awards for periods less than an hour. | 307 // Don't give engagement awards for periods less than an hour. |
| 311 if (elapsed_hours < 1) | 308 if (elapsed_hours < 1) |
| 312 return; | 309 return; |
| 313 if (elapsed_hours < kBudgetDurationInHours) | 310 if (elapsed_hours < kBudgetDurationInHours) |
| 314 ratio = elapsed_hours / kBudgetDurationInHours; | 311 ratio = elapsed_hours / kBudgetDurationInHours; |
| 315 } | 312 } |
| 316 | 313 |
| 317 // Update the last_engagement_award to the current time. If the origin wasn't | 314 // Update the last_engagement_award to the current time. If the origin wasn't |
| 318 // already in the map, this adds a new entry for it. | 315 // already in the map, this adds a new entry for it. |
| 319 budget_map_[origin.spec()].last_engagement_award = clock_->Now(); | 316 budget_map_[origin].last_engagement_award = clock_->Now(); |
| 320 | 317 |
| 321 // Add a new chunk of budget for the origin at the default expiration time. | 318 // Add a new chunk of budget for the origin at the default expiration time. |
| 322 base::Time expiration = | 319 base::Time expiration = |
| 323 clock_->Now() + base::TimeDelta::FromHours(kBudgetDurationInHours); | 320 clock_->Now() + base::TimeDelta::FromHours(kBudgetDurationInHours); |
| 324 budget_map_[origin.spec()].chunks.emplace_back(ratio * score, expiration); | 321 budget_map_[origin].chunks.emplace_back(ratio * score, expiration); |
| 325 | 322 |
| 326 // Any time we award engagement budget, which is done at most once an hour | 323 // Any time we award engagement budget, which is done at most once an hour |
| 327 // whenever any budget action is taken, record the budget. | 324 // whenever any budget action is taken, record the budget. |
| 328 double budget = GetBudget(origin); | 325 double budget = GetBudget(origin); |
| 329 UMA_HISTOGRAM_COUNTS_100("PushMessaging.BackgroundBudget", budget); | 326 UMA_HISTOGRAM_COUNTS_100("PushMessaging.BackgroundBudget", budget); |
| 330 } | 327 } |
| 331 | 328 |
| 332 // Cleans up budget in the cache. Relies on the caller eventually writing the | 329 // Cleans up budget in the cache. Relies on the caller eventually writing the |
| 333 // cache back to the database. | 330 // cache back to the database. |
| 334 bool BudgetDatabase::CleanupExpiredBudget(const GURL& origin) { | 331 bool BudgetDatabase::CleanupExpiredBudget(const url::Origin& origin) { |
| 335 if (!IsCached(origin)) | 332 if (!IsCached(origin)) |
| 336 return false; | 333 return false; |
| 337 | 334 |
| 338 base::Time now = clock_->Now(); | 335 base::Time now = clock_->Now(); |
| 339 BudgetChunks& chunks = budget_map_[origin.spec()].chunks; | 336 BudgetChunks& chunks = budget_map_[origin].chunks; |
| 340 auto cleanup_iter = chunks.begin(); | 337 auto cleanup_iter = chunks.begin(); |
| 341 | 338 |
| 342 // This relies on the list of chunks being in timestamp order. | 339 // This relies on the list of chunks being in timestamp order. |
| 343 while (cleanup_iter != chunks.end() && cleanup_iter->expiration <= now) | 340 while (cleanup_iter != chunks.end() && cleanup_iter->expiration <= now) |
| 344 cleanup_iter = chunks.erase(cleanup_iter); | 341 cleanup_iter = chunks.erase(cleanup_iter); |
| 345 | 342 |
| 346 // If the entire budget is empty now AND there have been no engagements | 343 // If the entire budget is empty now AND there have been no engagements |
| 347 // in the last kBudgetDurationInHours hours, remove this from the cache. | 344 // in the last kBudgetDurationInHours hours, remove this from the cache. |
| 348 if (chunks.empty() && | 345 if (chunks.empty() && |
| 349 budget_map_[origin.spec()].last_engagement_award < | 346 budget_map_[origin].last_engagement_award < |
| 350 clock_->Now() - base::TimeDelta::FromHours(kBudgetDurationInHours)) { | 347 clock_->Now() - base::TimeDelta::FromHours(kBudgetDurationInHours)) { |
| 351 budget_map_.erase(origin.spec()); | 348 budget_map_.erase(origin); |
| 352 return true; | 349 return true; |
| 353 } | 350 } |
| 354 | 351 |
| 355 // Although some things may have expired, there are some chunks still valid. | 352 // Although some things may have expired, there are some chunks still valid. |
| 356 // Don't write to the DB now, write either when all chunks expire or when the | 353 // Don't write to the DB now, write either when all chunks expire or when the |
| 357 // origin spends some budget. | 354 // origin spends some budget. |
| 358 return false; | 355 return false; |
| 359 } | 356 } |
| OLD | NEW |