[go: nahoru, domu]

blob: 1a2f0e3a82a4e5463280f3c9db03f675fb2315d1 [file] [log] [blame]
Alan Cutter5d14a632023-09-28 02:16:131// Copyright 2023 The Chromium Authors
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 <memory>
6
7#include "chrome/browser/web_applications/generated_icon_fix_util.h"
8
9#include "base/notreached.h"
10#include "base/numerics/safe_conversions.h"
11#include "base/strings/string_util.h"
12#include "base/values.h"
Alan Cutter07345692023-09-28 09:44:5713#include "chrome/browser/web_applications/web_app_registrar.h"
14#include "chrome/browser/web_applications/web_app_registry_update.h"
15#include "chrome/browser/web_applications/web_app_sync_bridge.h"
16#include "chrome/common/chrome_features.h"
Alan Cutter5d14a632023-09-28 02:16:1317#include "components/sync/base/time.h"
18
19namespace web_app {
20
Alan Cutter07345692023-09-28 09:44:5721namespace generated_icon_fix_util {
22
23namespace {
24
25constexpr base::TimeDelta kFixWindowDuration = base::Days(7);
Alan Cuttera027a832023-10-03 08:05:3326constexpr base::TimeDelta kFixAttemptThrottleDuration = base::Days(1);
27
28// Capping the number of attempts should be redundant with the throttle + window
29// but, because retries on failure are self propagating, have an explicit
30// attempt count to be extra safe. Ordinarily a constraint like this would be
31// CHECK'd but because these attempts run at start up it wouldn't be good for
32// stability.
33constexpr uint32_t kMaxAttemptCount =
34 kFixWindowDuration.IntDiv(kFixAttemptThrottleDuration);
35static_assert(kMaxAttemptCount == 7u);
Alan Cutter07345692023-09-28 09:44:5736
Arthur Sonzognife132ee2024-01-15 11:01:0437std::optional<base::Time> g_now_override_for_testing_;
Alan Cutter07345692023-09-28 09:44:5738
39base::Time Now() {
40 return g_now_override_for_testing_.value_or(base::Time::Now());
41}
42
43} // namespace
44
45bool IsValid(const GeneratedIconFix& generated_icon_fix) {
Alan Cutter5d14a632023-09-28 02:16:1346 return generated_icon_fix.has_source() &&
47 generated_icon_fix.source() != GeneratedIconFixSource_UNKNOWN &&
48 generated_icon_fix.has_window_start_time() &&
49 generated_icon_fix.has_attempt_count();
50}
51
Alan Cutter07345692023-09-28 09:44:5752base::Value ToDebugValue(const GeneratedIconFix* generated_icon_fix) {
Alan Cutter5d14a632023-09-28 02:16:1353 if (!generated_icon_fix) {
54 return base::Value();
55 }
56
57 base::Value::Dict debug_value;
Alan Cutter07345692023-09-28 09:44:5758 debug_value.Set("source", base::ToString(generated_icon_fix->source()));
Alan Cutter5d14a632023-09-28 02:16:1359 debug_value.Set("window_start_time",
60 base::ToString(syncer::ProtoTimeToTime(
61 generated_icon_fix->window_start_time())));
62 debug_value.Set("last_attempt_time",
63 generated_icon_fix->has_last_attempt_time()
64 ? base::Value(base::ToString(syncer::ProtoTimeToTime(
65 generated_icon_fix->last_attempt_time())))
66 : base::Value());
67 debug_value.Set("attempt_count", base::saturated_cast<int>(
68 generated_icon_fix->attempt_count()));
69 return base::Value(std::move(debug_value));
70}
71
Alan Cutter07345692023-09-28 09:44:5772void SetNowForTesting(base::Time now) {
73 g_now_override_for_testing_ = now;
74}
75
Alan Cuttera027a832023-10-03 08:05:3376bool HasRemainingAttempts(const WebApp& app) {
Arthur Sonzognife132ee2024-01-15 11:01:0477 const std::optional<GeneratedIconFix>& generated_icon_fix =
Alan Cuttera027a832023-10-03 08:05:3378 app.generated_icon_fix();
79 if (!generated_icon_fix.has_value()) {
80 return true;
81 }
82 return generated_icon_fix->attempt_count() < kMaxAttemptCount;
83}
84
Alan Cutter07345692023-09-28 09:44:5785bool IsWithinFixTimeWindow(const WebApp& app) {
Arthur Sonzognife132ee2024-01-15 11:01:0486 const std::optional<GeneratedIconFix>& generated_icon_fix =
Alan Cutter07345692023-09-28 09:44:5787 app.generated_icon_fix();
88 if (!generated_icon_fix.has_value()) {
89 return base::FeatureList::IsEnabled(
90 features::kWebAppSyncGeneratedIconRetroactiveFix);
91 }
92
93 base::TimeDelta duration_since_window_started =
94 Now() - syncer::ProtoTimeToTime(generated_icon_fix->window_start_time());
95 return duration_since_window_started <= kFixWindowDuration;
96}
97
98void EnsureFixTimeWindowStarted(WithAppResources& resources,
99 ScopedRegistryUpdate& update,
100 const webapps::AppId& app_id,
101 GeneratedIconFixSource source) {
102 if (resources.registrar()
103 .GetAppById(app_id)
104 ->generated_icon_fix()
105 .has_value()) {
106 return;
107 }
108 update->UpdateApp(app_id)->SetGeneratedIconFix(
109 CreateInitialTimeWindow(source));
110}
111
112GeneratedIconFix CreateInitialTimeWindow(GeneratedIconFixSource source) {
113 GeneratedIconFix generated_icon_fix;
114 generated_icon_fix.set_source(source);
115 generated_icon_fix.set_window_start_time(syncer::TimeToProtoTime(Now()));
116 generated_icon_fix.set_attempt_count(0);
117 return generated_icon_fix;
118}
119
Alan Cuttera027a832023-10-03 08:05:33120base::TimeDelta GetThrottleDuration(const WebApp& app) {
Arthur Sonzognife132ee2024-01-15 11:01:04121 const std::optional<GeneratedIconFix> generated_icon_fix =
Alan Cuttera027a832023-10-03 08:05:33122 app.generated_icon_fix();
123 if (!generated_icon_fix.has_value() ||
124 !generated_icon_fix->has_last_attempt_time()) {
125 return base::TimeDelta();
126 }
127
128 base::TimeDelta throttle_duration =
129 syncer::ProtoTimeToTime(generated_icon_fix->last_attempt_time()) +
130 kFixAttemptThrottleDuration - Now();
131 // Negative durations could cause us to skip ahead of other tasks already in
132 // the task queue when used in PostDelayedTask() so clamp to 0.
133 return throttle_duration.is_negative() ? base::TimeDelta()
134 : throttle_duration;
135}
136
Alan Cutter07345692023-09-28 09:44:57137void RecordFixAttempt(WithAppResources& resources,
138 ScopedRegistryUpdate& update,
139 const webapps::AppId& app_id,
140 GeneratedIconFixSource source) {
141 EnsureFixTimeWindowStarted(resources, update, app_id, source);
142 WebApp* app = update->UpdateApp(app_id);
143 GeneratedIconFix generated_icon_fix = app->generated_icon_fix().value();
144 generated_icon_fix.set_attempt_count(generated_icon_fix.attempt_count() + 1);
145 generated_icon_fix.set_last_attempt_time(syncer::TimeToProtoTime(Now()));
146 app->SetGeneratedIconFix(std::move(generated_icon_fix));
147}
148
149} // namespace generated_icon_fix_util
150
Alan Cutter5d14a632023-09-28 02:16:13151bool operator==(const GeneratedIconFix& a, const GeneratedIconFix& b) {
152 return a.SerializeAsString() == b.SerializeAsString();
153}
154
Alan Cutter07345692023-09-28 09:44:57155std::ostream& operator<<(std::ostream& out,
156 const GeneratedIconFixSource& source) {
157 switch (source) {
158 case GeneratedIconFixSource_UNKNOWN:
159 NOTREACHED();
160 return out << "Unknown";
161 case GeneratedIconFixSource_SYNC_INSTALL:
162 return out << "SyncInstall";
163 case GeneratedIconFixSource_RETROACTIVE:
164 return out << "Retroactive";
165 case GeneratedIconFixSource_MANIFEST_UPDATE:
166 return out << "ManifestUpdate";
167 }
168}
169
Alan Cutter5d14a632023-09-28 02:16:13170} // namespace web_app