blob: 3ec50f31924a25e164e15eda022613b5382761c6 [file] [log] [blame]
harkness883658b2016-07-18 11:37:531// Copyright 2016 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
Peter Beverlooafb1ab42018-08-20 13:03:455#include "chrome/browser/push_messaging/budget_database.h"
harkness883658b2016-07-18 11:37:536
Sebastien Marchandf1349f52019-01-25 03:16:417#include "base/bind.h"
fdorayba121422016-12-23 19:51:488#include "base/memory/ptr_util.h"
harkness6f6b41432016-09-08 09:17:289#include "base/metrics/histogram_macros.h"
Gabriel Charette44db1422018-08-06 11:19:3310#include "base/task/post_task.h"
harknessf8c93432016-08-08 15:41:5911#include "base/time/clock.h"
12#include "base/time/default_clock.h"
harknessc3fb0452016-09-02 14:08:3713#include "chrome/browser/engagement/site_engagement_score.h"
14#include "chrome/browser/engagement/site_engagement_service.h"
15#include "chrome/browser/profiles/profile.h"
Peter Beverlooafb1ab42018-08-20 13:03:4516#include "chrome/browser/push_messaging/budget.pb.h"
Tommy Nyquiste5a5b1a152019-01-17 06:54:3717#include "components/leveldb_proto/public/proto_database_provider.h"
harkness883658b2016-07-18 11:37:5318#include "content/public/browser/browser_thread.h"
19#include "url/gurl.h"
harkness03be8a1d2016-09-09 16:01:1220#include "url/origin.h"
harkness883658b2016-07-18 11:37:5321
harkness804b612a62016-07-27 12:53:3122using content::BrowserThread;
23
harkness883658b2016-07-18 11:37:5324namespace {
25
26// UMA are logged for the database with this string as part of the name.
harknessbea56c22016-08-16 07:23:0027// They will be LevelDB.*.BudgetManager. Changes here should be synchronized
28// with histograms.xml.
29const char kDatabaseUMAName[] = "BudgetManager";
harkness883658b2016-07-18 11:37:5330
harknessf8c93432016-08-08 15:41:5931// The default amount of time during which a budget will be valid.
harknessccdc3f3a2017-01-16 12:42:1732constexpr int kBudgetDurationInDays = 4;
33
34// The amount of budget that a maximally engaged site should receive per hour.
35// For context, silent push messages cost 2 each, so this allows 6 silent push
36// messages a day for a fully engaged site. See budget_manager.cc for costs of
37// various actions.
38constexpr double kMaximumHourlyBudget = 12.0 / 24.0;
harknessf8c93432016-08-08 15:41:5939
harkness883658b2016-07-18 11:37:5340} // namespace
41
Peter Beverlooafb1ab42018-08-20 13:03:4542BudgetState::BudgetState() = default;
43BudgetState::BudgetState(const BudgetState& other) = default;
44BudgetState::~BudgetState() = default;
45
46BudgetState& BudgetState::operator=(const BudgetState& other) = default;
47
48BudgetDatabase::BudgetInfo::BudgetInfo() = default;
harknesse0e26092016-08-24 06:29:3249
50BudgetDatabase::BudgetInfo::BudgetInfo(const BudgetInfo&& other)
51 : last_engagement_award(other.last_engagement_award) {
52 chunks = std::move(other.chunks);
53}
54
Peter Beverlooafb1ab42018-08-20 13:03:4555BudgetDatabase::BudgetInfo::~BudgetInfo() = default;
harknesse0e26092016-08-24 06:29:3256
Peter Beverlooafb1ab42018-08-20 13:03:4557BudgetDatabase::BudgetDatabase(Profile* profile)
harknessc3fb0452016-09-02 14:08:3758 : profile_(profile),
Tommy Nyquiste5a5b1a152019-01-17 06:54:3759 db_(leveldb_proto::ProtoDatabaseProvider::CreateUniqueDB<
60 budget_service::Budget>(base::CreateSequencedTaskRunnerWithTraits(
61 {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
62 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}))),
harknessf8c93432016-08-08 15:41:5963 clock_(base::WrapUnique(new base::DefaultClock)),
harkness883658b2016-07-18 11:37:5364 weak_ptr_factory_(this) {
Peter Beverlooafb1ab42018-08-20 13:03:4565 db_->Init(kDatabaseUMAName,
66 profile->GetPath().Append(FILE_PATH_LITERAL("BudgetDatabase")),
Chris Mumford29f2cf52017-09-21 00:35:4067 leveldb_proto::CreateSimpleOptions(),
68 base::BindOnce(&BudgetDatabase::OnDatabaseInit,
69 weak_ptr_factory_.GetWeakPtr()));
harkness883658b2016-07-18 11:37:5370}
71
Peter Beverlooafb1ab42018-08-20 13:03:4572BudgetDatabase::~BudgetDatabase() = default;
harkness883658b2016-07-18 11:37:5373
harkness03be8a1d2016-09-09 16:01:1274void BudgetDatabase::GetBudgetDetails(const url::Origin& origin,
tzikcf7bcd652017-06-15 04:19:3075 GetBudgetCallback callback) {
tzika8668f8c2017-06-26 08:58:0576 SyncCache(origin, base::BindOnce(&BudgetDatabase::GetBudgetAfterSync,
77 weak_ptr_factory_.GetWeakPtr(), origin,
tzik6d3cd7562018-02-21 12:07:2278 std::move(callback)));
harknesse0e26092016-08-24 06:29:3279}
80
harkness03be8a1d2016-09-09 16:01:1281void BudgetDatabase::SpendBudget(const url::Origin& origin,
Peter Beverlooafb1ab42018-08-20 13:03:4582 SpendBudgetCallback callback,
83 double amount) {
tzika8668f8c2017-06-26 08:58:0584 SyncCache(origin, base::BindOnce(&BudgetDatabase::SpendBudgetAfterSync,
85 weak_ptr_factory_.GetWeakPtr(), origin,
tzik6d3cd7562018-02-21 12:07:2286 amount, std::move(callback)));
harknessc3fb0452016-09-02 14:08:3787}
harknesse0e26092016-08-24 06:29:3288
harknessc3fb0452016-09-02 14:08:3789void BudgetDatabase::SetClockForTesting(std::unique_ptr<base::Clock> clock) {
90 clock_ = std::move(clock);
harknesse0e26092016-08-24 06:29:3291}
92
harkness804b612a62016-07-27 12:53:3193void BudgetDatabase::OnDatabaseInit(bool success) {
94 // TODO(harkness): Consider caching the budget database now?
95}
96
harkness03be8a1d2016-09-09 16:01:1297bool BudgetDatabase::IsCached(const url::Origin& origin) const {
98 return budget_map_.find(origin) != budget_map_.end();
harkness5186e6d822016-08-09 16:54:1499}
100
harkness03be8a1d2016-09-09 16:01:12101double BudgetDatabase::GetBudget(const url::Origin& origin) const {
harkness6f6b41432016-09-08 09:17:28102 double total = 0;
harkness03be8a1d2016-09-09 16:01:12103 auto iter = budget_map_.find(origin);
harkness6f6b41432016-09-08 09:17:28104 if (iter == budget_map_.end())
105 return total;
106
107 const BudgetInfo& info = iter->second;
108 for (const BudgetChunk& chunk : info.chunks)
109 total += chunk.amount;
110 return total;
111}
112
harkness804b612a62016-07-27 12:53:31113void BudgetDatabase::AddToCache(
harkness03be8a1d2016-09-09 16:01:12114 const url::Origin& origin,
tzikcf7bcd652017-06-15 04:19:30115 CacheCallback callback,
harkness804b612a62016-07-27 12:53:31116 bool success,
117 std::unique_ptr<budget_service::Budget> budget_proto) {
harkness0f9724a2016-09-13 14:43:12118 // If the database read failed or there's nothing to add, just return.
harknessf8c93432016-08-08 15:41:59119 if (!success || !budget_proto) {
tzikcf7bcd652017-06-15 04:19:30120 std::move(callback).Run(success);
harkness804b612a62016-07-27 12:53:31121 return;
122 }
123
harknessc3fb0452016-09-02 14:08:37124 // If there were two simultaneous loads, don't overwrite the cache value,
125 // which might have been updated after the previous load.
126 if (IsCached(origin)) {
tzikcf7bcd652017-06-15 04:19:30127 std::move(callback).Run(success);
harknessc3fb0452016-09-02 14:08:37128 return;
129 }
130
harkness804b612a62016-07-27 12:53:31131 // Add the data to the cache, converting from the proto format to an STL
132 // format which is better for removing things from the list.
harkness03be8a1d2016-09-09 16:01:12133 BudgetInfo& info = budget_map_[origin];
harknessbf8b3852016-08-10 18:42:02134 for (const auto& chunk : budget_proto->budget()) {
harknesse0e26092016-08-24 06:29:32135 info.chunks.emplace_back(chunk.amount(),
136 base::Time::FromInternalValue(chunk.expiration()));
harknessbf8b3852016-08-10 18:42:02137 }
harkness804b612a62016-07-27 12:53:31138
harknesse0e26092016-08-24 06:29:32139 info.last_engagement_award =
140 base::Time::FromInternalValue(budget_proto->engagement_last_updated());
harkness804b612a62016-07-27 12:53:31141
tzikcf7bcd652017-06-15 04:19:30142 std::move(callback).Run(success);
harkness804b612a62016-07-27 12:53:31143}
144
harkness03be8a1d2016-09-09 16:01:12145void BudgetDatabase::GetBudgetAfterSync(const url::Origin& origin,
tzikcf7bcd652017-06-15 04:19:30146 GetBudgetCallback callback,
harkness6f6b41432016-09-08 09:17:28147 bool success) {
Peter Beverlooafb1ab42018-08-20 13:03:45148 std::vector<BudgetState> predictions;
harkness6f6b41432016-09-08 09:17:28149
harkness804b612a62016-07-27 12:53:31150 // If the database wasn't able to read the information, return the
harkness6f6b41432016-09-08 09:17:28151 // failure and an empty predictions array.
harkness804b612a62016-07-27 12:53:31152 if (!success) {
Peter Beverlooafb1ab42018-08-20 13:03:45153 std::move(callback).Run(std::move(predictions));
harkness804b612a62016-07-27 12:53:31154 return;
155 }
156
harkness5186e6d822016-08-09 16:54:14157 // Now, build up the BudgetExpection. This is different from the format
harkness804b612a62016-07-27 12:53:31158 // in which the cache stores the data. The cache stores chunks of budget and
harkness6f6b41432016-09-08 09:17:28159 // when that budget expires. The mojo array describes a set of times
harkness804b612a62016-07-27 12:53:31160 // and the budget at those times.
harkness6f6b41432016-09-08 09:17:28161 double total = GetBudget(origin);
harkness804b612a62016-07-27 12:53:31162
harkness5186e6d822016-08-09 16:54:14163 // Always add one entry at the front of the list for the total budget now.
Peter Beverlooafb1ab42018-08-20 13:03:45164 BudgetState prediction;
165 prediction.budget_at = total;
166 prediction.time = clock_->Now().ToJsTime();
167 predictions.push_back(prediction);
harkness804b612a62016-07-27 12:53:31168
harkness6f6b41432016-09-08 09:17:28169 // Starting with the soonest expiring chunks, add entries for the
170 // expiration times going forward.
harkness03be8a1d2016-09-09 16:01:12171 const BudgetChunks& chunks = budget_map_[origin].chunks;
harkness6f6b41432016-09-08 09:17:28172 for (const auto& chunk : chunks) {
Peter Beverlooafb1ab42018-08-20 13:03:45173 BudgetState prediction;
harkness6f6b41432016-09-08 09:17:28174 total -= chunk.amount;
Peter Beverlooafb1ab42018-08-20 13:03:45175 prediction.budget_at = total;
176 prediction.time = chunk.expiration.ToJsTime();
177 predictions.push_back(prediction);
harkness6f6b41432016-09-08 09:17:28178 }
179
Peter Beverlooafb1ab42018-08-20 13:03:45180 std::move(callback).Run(std::move(predictions));
harkness804b612a62016-07-27 12:53:31181}
harknessf8c93432016-08-08 15:41:59182
harkness03be8a1d2016-09-09 16:01:12183void BudgetDatabase::SpendBudgetAfterSync(const url::Origin& origin,
harknessc3fb0452016-09-02 14:08:37184 double amount,
tzikcf7bcd652017-06-15 04:19:30185 SpendBudgetCallback callback,
harknessc3fb0452016-09-02 14:08:37186 bool success) {
187 if (!success) {
Peter Beverlooafb1ab42018-08-20 13:03:45188 std::move(callback).Run(false /* success */);
harknessc3fb0452016-09-02 14:08:37189 return;
190 }
191
harkness6f6b41432016-09-08 09:17:28192 // Get the current SES score, to generate UMA.
peter91882282017-05-25 17:57:33193 double score = GetSiteEngagementScoreForOrigin(origin);
harkness6f6b41432016-09-08 09:17:28194
harknessc3fb0452016-09-02 14:08:37195 // Walk the list of budget chunks to see if the origin has enough budget.
196 double total = 0;
harkness03be8a1d2016-09-09 16:01:12197 BudgetInfo& info = budget_map_[origin];
harknessc3fb0452016-09-02 14:08:37198 for (const BudgetChunk& chunk : info.chunks)
199 total += chunk.amount;
200
201 if (total < amount) {
harkness6f6b41432016-09-08 09:17:28202 UMA_HISTOGRAM_COUNTS_100("PushMessaging.SESForNoBudgetOrigin", score);
Peter Beverlooafb1ab42018-08-20 13:03:45203 std::move(callback).Run(false /* success */);
harknessc3fb0452016-09-02 14:08:37204 return;
harkness6f6b41432016-09-08 09:17:28205 } else if (total < amount * 2) {
206 UMA_HISTOGRAM_COUNTS_100("PushMessaging.SESForLowBudgetOrigin", score);
harknessc3fb0452016-09-02 14:08:37207 }
208
209 // Walk the chunks and remove enough budget to cover the needed amount.
210 double bill = amount;
211 for (auto iter = info.chunks.begin(); iter != info.chunks.end();) {
212 if (iter->amount > bill) {
213 iter->amount -= bill;
214 bill = 0;
215 break;
216 }
217 bill -= iter->amount;
218 iter = info.chunks.erase(iter);
219 }
220
221 // There should have been enough budget to cover the entire bill.
222 DCHECK_EQ(0, bill);
223
224 // Now that the cache is updated, write the data to the database.
harkness0f9724a2016-09-13 14:43:12225 WriteCachedValuesToDatabase(
tzikcf7bcd652017-06-15 04:19:30226 origin,
227 base::BindOnce(&BudgetDatabase::SpendBudgetAfterWrite,
228 weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
harkness0f9724a2016-09-13 14:43:12229}
230
231// This converts the bool value which is returned from the database to a Mojo
232// error type.
tzikcf7bcd652017-06-15 04:19:30233void BudgetDatabase::SpendBudgetAfterWrite(SpendBudgetCallback callback,
harkness0f9724a2016-09-13 14:43:12234 bool write_successful) {
harknessc3fb0452016-09-02 14:08:37235 // TODO(harkness): If the database write fails, the cache will be out of sync
236 // with the database. Consider ways to mitigate this.
harkness0f9724a2016-09-13 14:43:12237 if (!write_successful) {
Peter Beverlooafb1ab42018-08-20 13:03:45238 std::move(callback).Run(false /* success */);
harkness0f9724a2016-09-13 14:43:12239 return;
240 }
Peter Beverlooafb1ab42018-08-20 13:03:45241 std::move(callback).Run(true /* success */);
harknessf8c93432016-08-08 15:41:59242}
harkness5186e6d822016-08-09 16:54:14243
tzikcf7bcd652017-06-15 04:19:30244void BudgetDatabase::WriteCachedValuesToDatabase(const url::Origin& origin,
245 StoreBudgetCallback callback) {
harkness5186e6d822016-08-09 16:54:14246 // Create the data structures that are passed to the ProtoDatabase.
247 std::unique_ptr<
248 leveldb_proto::ProtoDatabase<budget_service::Budget>::KeyEntryVector>
249 entries(new leveldb_proto::ProtoDatabase<
250 budget_service::Budget>::KeyEntryVector());
251 std::unique_ptr<std::vector<std::string>> keys_to_remove(
252 new std::vector<std::string>());
253
254 // Each operation can either update the existing budget or remove the origin's
255 // budget information.
256 if (IsCached(origin)) {
257 // Build the Budget proto object.
258 budget_service::Budget budget;
harkness03be8a1d2016-09-09 16:01:12259 const BudgetInfo& info = budget_map_[origin];
harknesse0e26092016-08-24 06:29:32260 for (const auto& chunk : info.chunks) {
harkness5186e6d822016-08-09 16:54:14261 budget_service::BudgetChunk* budget_chunk = budget.add_budget();
harknessbf8b3852016-08-10 18:42:02262 budget_chunk->set_amount(chunk.amount);
263 budget_chunk->set_expiration(chunk.expiration.ToInternalValue());
harkness5186e6d822016-08-09 16:54:14264 }
harknesse0e26092016-08-24 06:29:32265 budget.set_engagement_last_updated(
266 info.last_engagement_award.ToInternalValue());
harkness03be8a1d2016-09-09 16:01:12267 entries->push_back(std::make_pair(origin.Serialize(), budget));
harkness5186e6d822016-08-09 16:54:14268 } else {
269 // If the origin doesn't exist in the cache, this is a remove operation.
harkness03be8a1d2016-09-09 16:01:12270 keys_to_remove->push_back(origin.Serialize());
harkness5186e6d822016-08-09 16:54:14271 }
272
273 // Send the updates to the database.
tzikcf7bcd652017-06-15 04:19:30274 db_->UpdateEntries(std::move(entries), std::move(keys_to_remove),
275 std::move(callback));
harkness5186e6d822016-08-09 16:54:14276}
277
harkness03be8a1d2016-09-09 16:01:12278void BudgetDatabase::SyncCache(const url::Origin& origin,
tzikcf7bcd652017-06-15 04:19:30279 CacheCallback callback) {
harknessc3fb0452016-09-02 14:08:37280 // If the origin isn't already cached, add it to the cache.
281 if (!IsCached(origin)) {
tzikcf7bcd652017-06-15 04:19:30282 CacheCallback add_callback = base::BindOnce(
283 &BudgetDatabase::SyncLoadedCache, weak_ptr_factory_.GetWeakPtr(),
284 origin, std::move(callback));
tzika8668f8c2017-06-26 08:58:05285 db_->GetEntry(origin.Serialize(),
286 base::BindOnce(&BudgetDatabase::AddToCache,
287 weak_ptr_factory_.GetWeakPtr(), origin,
tzik6d3cd7562018-02-21 12:07:22288 std::move(add_callback)));
harkness5186e6d822016-08-09 16:54:14289 return;
harknessc3fb0452016-09-02 14:08:37290 }
tzikcf7bcd652017-06-15 04:19:30291 SyncLoadedCache(origin, std::move(callback), true /* success */);
harknessc3fb0452016-09-02 14:08:37292}
293
harkness03be8a1d2016-09-09 16:01:12294void BudgetDatabase::SyncLoadedCache(const url::Origin& origin,
tzikcf7bcd652017-06-15 04:19:30295 CacheCallback callback,
harknessc3fb0452016-09-02 14:08:37296 bool success) {
297 if (!success) {
tzikcf7bcd652017-06-15 04:19:30298 std::move(callback).Run(false /* success */);
harknessc3fb0452016-09-02 14:08:37299 return;
300 }
301
harknessc3fb0452016-09-02 14:08:37302 // Now, cleanup any expired budget chunks for the origin.
303 bool needs_write = CleanupExpiredBudget(origin);
304
harkness6f6b41432016-09-08 09:17:28305 // Get the SES score and add engagement budget for the site.
306 AddEngagementBudget(origin);
307
harknessc3fb0452016-09-02 14:08:37308 if (needs_write)
tzikcf7bcd652017-06-15 04:19:30309 WriteCachedValuesToDatabase(origin, std::move(callback));
harknessc3fb0452016-09-02 14:08:37310 else
tzikcf7bcd652017-06-15 04:19:30311 std::move(callback).Run(success);
harknessc3fb0452016-09-02 14:08:37312}
313
harkness03be8a1d2016-09-09 16:01:12314void BudgetDatabase::AddEngagementBudget(const url::Origin& origin) {
harknessccdc3f3a2017-01-16 12:42:17315 // Calculate how much budget should be awarded. The award depends on the
316 // time elapsed since the last award and the SES score.
317 // By default, give the origin kBudgetDurationInDays worth of budget, but
318 // reduce that if budget has already been given during that period.
319 base::TimeDelta elapsed = base::TimeDelta::FromDays(kBudgetDurationInDays);
harknessc3fb0452016-09-02 14:08:37320 if (IsCached(origin)) {
harknessccdc3f3a2017-01-16 12:42:17321 elapsed = clock_->Now() - budget_map_[origin].last_engagement_award;
harknessc3fb0452016-09-02 14:08:37322 // Don't give engagement awards for periods less than an hour.
harknessccdc3f3a2017-01-16 12:42:17323 if (elapsed.InHours() < 1)
harknessc3fb0452016-09-02 14:08:37324 return;
harknessccdc3f3a2017-01-16 12:42:17325 // Cap elapsed time to the budget duration.
326 if (elapsed.InDays() > kBudgetDurationInDays)
327 elapsed = base::TimeDelta::FromDays(kBudgetDurationInDays);
harknessc3fb0452016-09-02 14:08:37328 }
329
harknessccdc3f3a2017-01-16 12:42:17330 // Get the current SES score, and calculate the hourly budget for that score.
harknessccdc3f3a2017-01-16 12:42:17331 double hourly_budget = kMaximumHourlyBudget *
peter91882282017-05-25 17:57:33332 GetSiteEngagementScoreForOrigin(origin) /
333 SiteEngagementService::GetMaxPoints();
harknessccdc3f3a2017-01-16 12:42:17334
harknessc3fb0452016-09-02 14:08:37335 // Update the last_engagement_award to the current time. If the origin wasn't
336 // already in the map, this adds a new entry for it.
harkness03be8a1d2016-09-09 16:01:12337 budget_map_[origin].last_engagement_award = clock_->Now();
harknessc3fb0452016-09-02 14:08:37338
339 // Add a new chunk of budget for the origin at the default expiration time.
340 base::Time expiration =
harknessccdc3f3a2017-01-16 12:42:17341 clock_->Now() + base::TimeDelta::FromDays(kBudgetDurationInDays);
342 budget_map_[origin].chunks.emplace_back(elapsed.InHours() * hourly_budget,
343 expiration);
harkness6f6b41432016-09-08 09:17:28344
345 // Any time we award engagement budget, which is done at most once an hour
346 // whenever any budget action is taken, record the budget.
347 double budget = GetBudget(origin);
348 UMA_HISTOGRAM_COUNTS_100("PushMessaging.BackgroundBudget", budget);
harknessc3fb0452016-09-02 14:08:37349}
350
351// Cleans up budget in the cache. Relies on the caller eventually writing the
352// cache back to the database.
harkness03be8a1d2016-09-09 16:01:12353bool BudgetDatabase::CleanupExpiredBudget(const url::Origin& origin) {
harknessc3fb0452016-09-02 14:08:37354 if (!IsCached(origin))
355 return false;
harkness5186e6d822016-08-09 16:54:14356
357 base::Time now = clock_->Now();
harkness03be8a1d2016-09-09 16:01:12358 BudgetChunks& chunks = budget_map_[origin].chunks;
harkness5186e6d822016-08-09 16:54:14359 auto cleanup_iter = chunks.begin();
360
361 // This relies on the list of chunks being in timestamp order.
harknessbf8b3852016-08-10 18:42:02362 while (cleanup_iter != chunks.end() && cleanup_iter->expiration <= now)
harkness5186e6d822016-08-09 16:54:14363 cleanup_iter = chunks.erase(cleanup_iter);
364
harknesse0e26092016-08-24 06:29:32365 // If the entire budget is empty now AND there have been no engagements
harknessccdc3f3a2017-01-16 12:42:17366 // in the last kBudgetDurationInDays days, remove this from the cache.
harknesse0e26092016-08-24 06:29:32367 if (chunks.empty() &&
harkness03be8a1d2016-09-09 16:01:12368 budget_map_[origin].last_engagement_award <
harknessccdc3f3a2017-01-16 12:42:17369 clock_->Now() - base::TimeDelta::FromDays(kBudgetDurationInDays)) {
harkness03be8a1d2016-09-09 16:01:12370 budget_map_.erase(origin);
harknessc3fb0452016-09-02 14:08:37371 return true;
372 }
373
374 // Although some things may have expired, there are some chunks still valid.
375 // Don't write to the DB now, write either when all chunks expire or when the
376 // origin spends some budget.
377 return false;
harkness5186e6d822016-08-09 16:54:14378}
peter91882282017-05-25 17:57:33379
380double BudgetDatabase::GetSiteEngagementScoreForOrigin(
381 const url::Origin& origin) const {
382 if (profile_->IsOffTheRecord())
383 return 0;
384
385 return SiteEngagementService::Get(profile_)->GetScore(origin.GetURL());
386}